From cff9cdbd3b6f263b89333ed10c2596e4379ab2f6 Mon Sep 17 00:00:00 2001 From: LukasKerk <70198528+LukasKerk@users.noreply.github.com> Date: Thu, 20 May 2021 13:27:03 +0200 Subject: [PATCH 1/6] Feature/fancy pointers (#1) * Started to add fancy pointer support to the sparse array class and a lot of tests to ensure that nothing is broken. * Fixxed a bug in the move construction test. * Started to work on sparse_hash * Some more changes for the iterators. * 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. * Integrated boost::to_address directly into the code. * A bit of tidy up. Now the fancy pointer tests are integrated into the "normal" test suit and can be compiled with C++-11. * added test for value() with fancy pointer * added missing const * Fixxed a bug in the OffsetAllocator, also did some work on erase. * added Remove_Const struct for overloading const_cast with @LukasKerk * Added the "const_cast" for boost offset pointers in its own header. * Fixed a typo in a test output. * Corrected comments. Co-authored-by: Alexander Bigerl --- include/tsl/boost_offset_pointer.h | 24 +++ include/tsl/sparse_hash.h | 116 ++++++++--- tests/CMakeLists.txt | 21 +- tests/fancy_pointer/CustomAllocator.h | 46 +++++ tests/fancy_pointer/sparse_array_tests.cpp | 135 +++++++++++++ tests/fancy_pointer/sparse_hash_map_tests.cpp | 176 +++++++++++++++++ tests/fancy_pointer/sparse_hash_set_tests.cpp | 185 ++++++++++++++++++ 7 files changed, 667 insertions(+), 36 deletions(-) create mode 100644 include/tsl/boost_offset_pointer.h create mode 100644 tests/fancy_pointer/CustomAllocator.h create mode 100644 tests/fancy_pointer/sparse_array_tests.cpp create mode 100644 tests/fancy_pointer/sparse_hash_map_tests.cpp create mode 100644 tests/fancy_pointer/sparse_hash_set_tests.cpp 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 96f754c..e383b4e 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -176,7 +176,58 @@ inline int popcount(unsigned int x) { return fallback_popcount(x); } #endif } // namespace detail_popcount + +/* 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 T remove(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. + * This specific implementation is based on boost 1.71.0. + */ +#if __cplusplus >= 201400L // 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); + } +#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->()); + } +#endif + template struct make_void { @@ -293,8 +344,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 = @@ -585,7 +639,7 @@ class sparse_array { } static iterator mutable_iterator(const_iterator pos) { - return const_cast(pos); + return ::tsl::Remove_Const::template remove(pos); } template @@ -673,18 +727,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, detail_sparse_hash::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, detail_sparse_hash::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 +862,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 +998,7 @@ class sparse_array { } private: - value_type *m_values; + pointer m_values; bitmap_type m_bitmap_vals; bitmap_type m_bitmap_deleted_vals; @@ -1009,15 +1063,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 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; @@ -1064,10 +1118,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 {} @@ -1103,24 +1157,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); } 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()) { + //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()) { + 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; @@ -1343,12 +1399,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()) { + //vector iterator with fancy pointers have a problem with -> + while (begin != m_sparse_buckets_data.end() && (*begin).empty()) { ++begin; } + //vector iterator with fancy pointers have a problem with -> return iterator(begin, (begin != m_sparse_buckets_data.end()) - ? begin->begin() + ? (*begin).begin() : nullptr); } @@ -1356,12 +1414,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()) { + //vector iterator with fancy pointers have a problem with -> + 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); } @@ -1488,23 +1547,24 @@ class sparse_hash : private Allocator, */ iterator erase(iterator pos) { tsl_sh_assert(pos != end() && m_nb_elements > 0); + //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); + (*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/CMakeLists.txt b/tests/CMakeLists.txt index 26cea96..cb1047f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,12 +2,17 @@ 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" + "fancy_pointer/sparse_array_tests.cpp" + "fancy_pointer/sparse_hash_set_tests.cpp" + "fancy_pointer/CustomAllocator.h" + "fancy_pointer/sparse_hash_map_tests.cpp" + "../include/tsl/boost_offset_pointer.h") target_compile_features(tsl_sparse_map_tests PRIVATE cxx_std_11) @@ -19,8 +24,8 @@ 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) # 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) \ No newline at end of file diff --git a/tests/fancy_pointer/CustomAllocator.h b/tests/fancy_pointer/CustomAllocator.h new file mode 100644 index 0000000..7340fef --- /dev/null +++ b/tests/fancy_pointer/CustomAllocator.h @@ -0,0 +1,46 @@ +/** @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 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 { + using value_type = T; + template using offset_ptr = boost::interprocess::offset_ptr

; + using pointer = offset_ptr; + using const_pointer = offset_ptr; + using void_pointer = offset_ptr; + using const_void_pointer = offset_ptr; + using difference_type = typename offset_ptr::difference_type; + + OffsetAllocator() noexcept = default; + OffsetAllocator(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); + } +}; + +#endif //TSL_SPARSE_MAP_TESTS_CUSTOMALLOCATOR_H \ 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..8f992b1 --- /dev/null +++ b/tests/fancy_pointer/sparse_array_tests.cpp @@ -0,0 +1,135 @@ +/** @file + * @brief Checks for fancy pointer support in the sparse_array implementation. + */ + +#include +#include +#include "CustomAllocator.h" + +// Globals +constexpr auto MAX_INDEX = 32; //BITMAP_NB_BITS + +/* 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; +} + +template +void construction() { + typename T::Allocator a; + typename T::Array test(MAX_INDEX, a); + test.clear(a); //needed because destructor asserts +} + +namespace details { + template + 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); + } + return arr; + } + + template + 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; + } + 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; + //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"); + 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); +} + + +/* + * This are the types you can give the tests as template parameters. + */ +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; +}; + + + +/* 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>();} +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_map_tests.cpp b/tests/fancy_pointer/sparse_hash_map_tests.cpp new file mode 100644 index 0000000..f84de72 --- /dev/null +++ b/tests/fancy_pointer/sparse_hash_map_tests.cpp @@ -0,0 +1,176 @@ +/** @file + * @brief Checks for fancy pointer support in the sparse_hash implementation for pair values (maps). + */ + +#include +#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; + const key_type &operator()(std::pair const &key_value) const noexcept { + return key_value.first; + } + 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; + } + 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 + typename T::Map 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 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 + 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 + 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 +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()), + "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 { + 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(fancy_pointers) +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(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; + 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 new file mode 100644 index 0000000..4d42353 --- /dev/null +++ b/tests/fancy_pointer/sparse_hash_set_tests.cpp @@ -0,0 +1,185 @@ +/** @file + * @brief Checks for fancy pointer support in the sparse_hash implementation for single values (sets). + */ + +#include +#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; + 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 + 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. + * 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](typename Set::value_type 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); + 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()), + "iterating over the set didn't work"); +} + + +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 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()); + // force non-const iterator + 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()); + //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 +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(fancy_pointers) +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});} +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_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});} +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_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; + 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 5989556346adc5db710d4632ceda4bb280af8144 Mon Sep 17 00:00:00 2001 From: LukasKerk <70198528+LukasKerk@users.noreply.github.com> Date: Thu, 27 May 2021 14:22:00 +0200 Subject: [PATCH 2/6] Feature/scoped allocator adaptor (#2) * Initial commit. First few tests, found one problem. * Now scoped allocators can be used. However emplace still does not work as it should. * Some cleanup. * Formatting and removing comments. --- include/tsl/sparse_hash.h | 15 +- tests/CMakeLists.txt | 4 +- .../sparse_array_tests.cpp | 138 ++++++++++++++++++ .../sparse_hash_set_tests.cpp | 51 +++++++ 4 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 tests/scoped_allocator_adaptor/sparse_array_tests.cpp create mode 100644 tests/scoped_allocator_adaptor/sparse_hash_set_tests.cpp diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index e383b4e..fd9d64b 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -437,6 +437,9 @@ class sparse_array { m_capacity(0), m_last_array(false) {} + //needed for "is_constructible" with no parameters + sparse_array(std::allocator_arg_t, Allocator const&) noexcept : sparse_array() {} + explicit sparse_array(bool last_bucket) noexcept : m_values(nullptr), m_bitmap_vals(0), @@ -445,7 +448,8 @@ class sparse_array { m_capacity(0), m_last_array(last_bucket) {} - sparse_array(size_type capacity, Allocator &alloc) + //const Allocator needed for MoveInsertable requirement + sparse_array(size_type capacity, Allocator const &const_alloc) : m_values(nullptr), m_bitmap_vals(0), m_bitmap_deleted_vals(0), @@ -453,13 +457,15 @@ class sparse_array { m_capacity(capacity), m_last_array(false) { if (m_capacity > 0) { + auto alloc = const_cast(const_alloc); m_values = alloc.allocate(m_capacity); tsl_sh_assert(m_values != nullptr); // allocate should throw if there is a failure } } - sparse_array(const sparse_array &other, Allocator &alloc) + //const Allocator needed for MoveInsertable requirement + sparse_array(const sparse_array &other, Allocator const &const_alloc) : m_values(nullptr), m_bitmap_vals(other.m_bitmap_vals), m_bitmap_deleted_vals(other.m_bitmap_deleted_vals), @@ -471,6 +477,7 @@ class sparse_array { return; } + auto alloc = const_cast(const_alloc); m_values = alloc.allocate(m_capacity); tsl_sh_assert(m_values != nullptr); // allocate should throw if there is a failure @@ -499,7 +506,8 @@ class sparse_array { other.m_capacity = 0; } - sparse_array(sparse_array &&other, Allocator &alloc) + //const Allocator needed for MoveInsertable requirement + sparse_array(sparse_array &&other, Allocator const &const_alloc) : m_values(nullptr), m_bitmap_vals(other.m_bitmap_vals), m_bitmap_deleted_vals(other.m_bitmap_deleted_vals), @@ -511,6 +519,7 @@ class sparse_array { return; } + auto alloc = const_cast(const_alloc); m_values = alloc.allocate(m_capacity); tsl_sh_assert(m_values != nullptr); // allocate should throw if there is a failure diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cb1047f..622d0b3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,7 +12,9 @@ add_executable(tsl_sparse_map_tests "main.cpp" "fancy_pointer/sparse_hash_set_tests.cpp" "fancy_pointer/CustomAllocator.h" "fancy_pointer/sparse_hash_map_tests.cpp" - "../include/tsl/boost_offset_pointer.h") + "../include/tsl/boost_offset_pointer.h" + "scoped_allocator_adaptor/sparse_array_tests.cpp" + "scoped_allocator_adaptor/sparse_hash_set_tests.cpp") target_compile_features(tsl_sparse_map_tests PRIVATE cxx_std_11) diff --git a/tests/scoped_allocator_adaptor/sparse_array_tests.cpp b/tests/scoped_allocator_adaptor/sparse_array_tests.cpp new file mode 100644 index 0000000..0145c86 --- /dev/null +++ b/tests/scoped_allocator_adaptor/sparse_array_tests.cpp @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include + +// Globals +constexpr auto MAX_INDEX = 32; // BITMAP_NB_BITS + +template void compilation() { typename T::Array test; } + +template void construction() { + typename T::Allocator a; + typename T::Array test(MAX_INDEX, a); + test.clear(a); +} + +template +void set(std::initializer_list l) { + typename T::Allocator a; + typename T::Array array(MAX_INDEX, a); + std::vector check; + check.reserve(l.size()); + std::size_t counter = 0; + for (auto const &value : l) { + array.set(a, counter++, value); + check.emplace_back(value); + } + BOOST_TEST_REQUIRE(std::equal(array.begin(), array.end(), check.begin()), + "'set' did not create the correct order of items"); + array.clear(a); +} + +template void uses_allocator() { + BOOST_TEST_REQUIRE( + (std::uses_allocator::value), + "uses_allocator returns false"); +} + +template +void trailing_allocator_convention(Args...) { + using Alloc = typename T::Allocator; + BOOST_TEST_REQUIRE( + (std::is_constructible::value), + "trailing_allocator thinks construction is not possible"); +} + +template void trailing_allocator_convention_without_parameters() { + using Alloc = typename std::allocator_traits< + typename T::Allocator>::template rebind_alloc; + BOOST_TEST_REQUIRE( + (std::is_constructible::value), + "trailing_allocator thinks construction is not possible"); +} + +/** This test creates a memory leak. + * However it is only one single sparse_array and with this, the test is + * simpler. + */ +template +void is_move_insertable(std::initializer_list l) { + using A = typename std::allocator_traits< + typename T::Allocator>::template rebind_alloc; + A m; + auto p = m.allocate(1); + typename T::Allocator ArrayAlloc; + typename T::Array rv(MAX_INDEX, ArrayAlloc); + std::size_t counter = 0; + for (auto const &value : l) { + rv.set(ArrayAlloc, counter++, value); + } + std::cout << "Before\n"; + std::allocator_traits::construct(m, p, std::move(rv)); + std::cout << "After\n"; + rv.clear(ArrayAlloc); +} + +/** This test creates a memory leak. + * However it is only one single sparse_array and with this, the test is + * simpler. + */ +template void is_default_insertable() { + using A = typename std::allocator_traits< + typename T::Allocator>::template rebind_alloc; + A m; + typename T::Array *p = m.allocate(1); + std::allocator_traits::construct(m, p); +} + +template +struct NORMAL { + using value_type = T; + using Allocator = std::allocator; + using Array = tsl::detail_sparse_hash::sparse_array; +}; + +template +struct SCOPED { + using value_type = T; + using Allocator = std::scoped_allocator_adaptor>; + using Array = tsl::detail_sparse_hash::sparse_array; +}; + +BOOST_AUTO_TEST_SUITE(scoped_allocators) +BOOST_AUTO_TEST_SUITE(sparse_array_tests) + +BOOST_AUTO_TEST_CASE(normal_compilation) { compilation>(); } +BOOST_AUTO_TEST_CASE(normal_construction) { construction>(); } +BOOST_AUTO_TEST_CASE(normal_set) { set>({0, 1, 2, 3, 4}); } +BOOST_AUTO_TEST_CASE(normal_uses_allocator) { uses_allocator>(); } +BOOST_AUTO_TEST_CASE(normal_trailing_allocator_convention) { + trailing_allocator_convention>(0); +} +BOOST_AUTO_TEST_CASE(normal_is_move_insertable) { + is_move_insertable>({0, 1, 2, 3, 4, 5}); +} +BOOST_AUTO_TEST_CASE(normal_is_default_insertable) { + is_default_insertable>(); +} + +BOOST_AUTO_TEST_CASE(scoped_compilation) { compilation>(); } +BOOST_AUTO_TEST_CASE(scoped_construction) { construction>(); } +BOOST_AUTO_TEST_CASE(scoped_set) { set>({0, 1, 2, 3, 4}); } +BOOST_AUTO_TEST_CASE(scoped_uses_allocator) { uses_allocator>(); } +BOOST_AUTO_TEST_CASE(scoped_trailing_allocator_convention) { + trailing_allocator_convention>(0); +} +BOOST_AUTO_TEST_CASE(scoped_is_move_insertable) { + is_move_insertable>({0, 1, 2, 3, 4, 5}); +} +BOOST_AUTO_TEST_CASE(scoped_is_default_insertable) { + is_default_insertable>(); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/scoped_allocator_adaptor/sparse_hash_set_tests.cpp b/tests/scoped_allocator_adaptor/sparse_hash_set_tests.cpp new file mode 100644 index 0000000..4fbf569 --- /dev/null +++ b/tests/scoped_allocator_adaptor/sparse_hash_set_tests.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +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, details::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>; +} // namespace details + +template void construction() { + using Type = typename T::value_type; + typename T::Set(T::Set::DEFAULT_INIT_BUCKET_COUNT, std::hash(), + std::equal_to(), typename T::Allocator(), + T::Set::DEFAULT_MAX_LOAD_FACTOR); +} + + +template +struct NORMAL { + using value_type = T; + using Allocator = std::allocator; + using Set = details::sparse_set; +}; + +template +struct SCOPED { + using value_type = T; + using Allocator = std::scoped_allocator_adaptor>; + using Set = details::sparse_set; +}; + +BOOST_AUTO_TEST_SUITE(scoped_allocators) +BOOST_AUTO_TEST_SUITE(sparse_hash_set_tests) + +BOOST_AUTO_TEST_CASE(normal_construction){construction>();} + +BOOST_AUTO_TEST_CASE(scoped_construction){construction>();} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 657294713848989d8ae092638458d3665068342a Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 27 May 2021 15:34:41 +0200 Subject: [PATCH 3/6] (Hopefully) fixed the travis problem. BOOST_TEST_REQUIRE seems to be a pretty recent addition to the boost unittest framework. It is now replaced by BOOST_REQUIRE --- tests/fancy_pointer/sparse_array_tests.cpp | 16 ++++----- tests/fancy_pointer/sparse_hash_map_tests.cpp | 24 ++++++++----- tests/fancy_pointer/sparse_hash_set_tests.cpp | 36 +++++++++++-------- .../sparse_array_tests.cpp | 19 +++++----- 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/tests/fancy_pointer/sparse_array_tests.cpp b/tests/fancy_pointer/sparse_array_tests.cpp index 8f992b1..6e590c9 100644 --- a/tests/fancy_pointer/sparse_array_tests.cpp +++ b/tests/fancy_pointer/sparse_array_tests.cpp @@ -51,8 +51,8 @@ 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"); + //'set' did not create the correct order of items + BOOST_REQUIRE(std::equal(test.begin(), test.end(), check.begin())); test.clear(a); //needed because destructor asserts } @@ -63,8 +63,8 @@ void copy_construction() { 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"); + //'copy' changed the order of the items + BOOST_REQUIRE(std::equal(copy.begin(), copy.end(), check.begin())); test.clear(a); copy.clear(a); } @@ -76,8 +76,8 @@ void move_construction() { 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"); + //'move' changed the order of the items + BOOST_REQUIRE(std::equal(moved_to.begin(), moved_to.end(), check.begin())); moved_to.clear(a); } @@ -86,8 +86,8 @@ 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"); + //const iterator has the wrong type + BOOST_REQUIRE((std::is_same::value)); test.clear(a); } diff --git a/tests/fancy_pointer/sparse_hash_map_tests.cpp b/tests/fancy_pointer/sparse_hash_map_tests.cpp index f84de72..087fadf 100644 --- a/tests/fancy_pointer/sparse_hash_map_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_map_tests.cpp @@ -81,21 +81,24 @@ 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"); + //'insert' did not create exactly the values needed + BOOST_REQUIRE(details::is_equal(map, l)); } 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"); + //'insert' with iterators did not create exactly the values needed + BOOST_REQUIRE(details::is_equal(map, l)); } 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"); + //iterator cannot access single value + BOOST_REQUIRE( (*(map.begin()) == single_value)); } template @@ -106,9 +109,9 @@ void iterator_access_multi(std::initializer_list 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()), - "iterating over the map didn't work"); + //iterating over the map didn't work + BOOST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), + map_sorted.begin())); } template @@ -120,7 +123,8 @@ void value(std::initializer_list l, typename T::value_ty 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"); + //changing a single value didn't work + BOOST_REQUIRE(details::is_equal(map, check)); } @@ -168,8 +172,10 @@ BOOST_AUTO_TEST_CASE(full_map) { 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"); + //size did not match + BOOST_REQUIRE(data.size() == map.size()); + //map did not contain all values + BOOST_REQUIRE(std::all_of(data.begin(), data.end(), check)); } 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 4d42353..4560825 100644 --- a/tests/fancy_pointer/sparse_hash_set_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_set_tests.cpp @@ -54,21 +54,24 @@ 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"); + //'insert' did not create exactly the values needed + BOOST_REQUIRE(details::is_equal(set, l)); } 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"); + //'insert' with iterators did not create exactly the values needed + BOOST_REQUIRE(details::is_equal(set, l)); } template void iterator_access(typename T::value_type single_value) { auto set = details::default_construct_set(); set.insert(single_value); - BOOST_TEST_REQUIRE(*(set.begin()) == single_value, "iterator cannot access single value"); + //iterator cannot access single value + BOOST_REQUIRE(*(set.begin()) == single_value); } template @@ -79,9 +82,9 @@ void iterator_access_multi(std::initializer_list 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()), - "iterating over the set didn't work"); + //iterating over the set didn't work + BOOST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), + set_sorted.begin())); } @@ -93,9 +96,9 @@ void const_iterator_access_multi(std::initializer_list 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"); + //const iterating over the set didn't work + BOOST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), + set_sorted.begin())); } template @@ -104,7 +107,8 @@ 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), "find did not work as expected"); + //find did not work as expected + BOOST_REQUIRE((found == is_in_list)); } template @@ -116,7 +120,8 @@ void erase(std::initializer_list l, typename T::value_ty 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"); + //erase did not work as expected + BOOST_REQUIRE(details::is_equal(set, l)); } template @@ -128,7 +133,8 @@ void erase_with_const_iter(std::initializer_list l, type 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"); + //erase did not work as expected + BOOST_REQUIRE(details::is_equal(set, l)); } @@ -177,8 +183,10 @@ BOOST_AUTO_TEST_CASE(full_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"); + //size did not match + BOOST_REQUIRE(data.size() == set.size()); + //Set did not contain all values + BOOST_REQUIRE(std::all_of(data.begin(), data.end(), check)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/scoped_allocator_adaptor/sparse_array_tests.cpp b/tests/scoped_allocator_adaptor/sparse_array_tests.cpp index 0145c86..79101f1 100644 --- a/tests/scoped_allocator_adaptor/sparse_array_tests.cpp +++ b/tests/scoped_allocator_adaptor/sparse_array_tests.cpp @@ -28,31 +28,28 @@ void set(std::initializer_list l) { array.set(a, counter++, value); check.emplace_back(value); } - BOOST_TEST_REQUIRE(std::equal(array.begin(), array.end(), check.begin()), - "'set' did not create the correct order of items"); + //'set' did not create the correct order of items + BOOST_REQUIRE(std::equal(array.begin(), array.end(), check.begin())); array.clear(a); } template void uses_allocator() { - BOOST_TEST_REQUIRE( - (std::uses_allocator::value), - "uses_allocator returns false"); + //uses_allocator returns false + BOOST_REQUIRE((std::uses_allocator::value)); } template void trailing_allocator_convention(Args...) { using Alloc = typename T::Allocator; - BOOST_TEST_REQUIRE( - (std::is_constructible::value), - "trailing_allocator thinks construction is not possible"); + //trailing_allocator thinks construction is not possible + BOOST_REQUIRE((std::is_constructible::value)); } template void trailing_allocator_convention_without_parameters() { using Alloc = typename std::allocator_traits< typename T::Allocator>::template rebind_alloc; - BOOST_TEST_REQUIRE( - (std::is_constructible::value), - "trailing_allocator thinks construction is not possible"); + //trailing_allocator thinks construction is not possible + BOOST_REQUIRE((std::is_constructible::value)); } /** This test creates a memory leak. From eb02f6aba44279aaf7ab8c1f8ca8963482d2eb7d Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 27 May 2021 16:53:53 +0200 Subject: [PATCH 4/6] Fixed the memory leak in two tests. --- .../sparse_array_tests.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/tests/scoped_allocator_adaptor/sparse_array_tests.cpp b/tests/scoped_allocator_adaptor/sparse_array_tests.cpp index 79101f1..a612696 100644 --- a/tests/scoped_allocator_adaptor/sparse_array_tests.cpp +++ b/tests/scoped_allocator_adaptor/sparse_array_tests.cpp @@ -52,38 +52,32 @@ template void trailing_allocator_convention_without_parameters() { BOOST_REQUIRE((std::is_constructible::value)); } -/** This test creates a memory leak. - * However it is only one single sparse_array and with this, the test is - * simpler. - */ template void is_move_insertable(std::initializer_list l) { using A = typename std::allocator_traits< typename T::Allocator>::template rebind_alloc; A m; - auto p = m.allocate(1); + auto p = std::allocator_traits::allocate(m, 1); typename T::Allocator ArrayAlloc; typename T::Array rv(MAX_INDEX, ArrayAlloc); std::size_t counter = 0; for (auto const &value : l) { rv.set(ArrayAlloc, counter++, value); } - std::cout << "Before\n"; std::allocator_traits::construct(m, p, std::move(rv)); - std::cout << "After\n"; rv.clear(ArrayAlloc); + p->clear(ArrayAlloc); + std::allocator_traits::destroy(m, p); + std::allocator_traits::deallocate(m, p, 1); } -/** This test creates a memory leak. - * However it is only one single sparse_array and with this, the test is - * simpler. - */ template void is_default_insertable() { using A = typename std::allocator_traits< typename T::Allocator>::template rebind_alloc; A m; - typename T::Array *p = m.allocate(1); + typename T::Array *p = std::allocator_traits::allocate(m, 1); std::allocator_traits::construct(m, p); + std::allocator_traits::deallocate(m, p, 1); } template From bde90b951b70a7d9e31265cb5c5a0aece0f69f4b Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 3 Jun 2021 14:54:52 +0200 Subject: [PATCH 5/6] Removed the custom tests and header file. --- include/tsl/boost_offset_pointer.h | 24 --- include/tsl/sparse_hash.h | 3 +- tests/CMakeLists.txt | 9 +- tests/fancy_pointer/CustomAllocator.h | 46 ----- tests/fancy_pointer/sparse_array_tests.cpp | 135 ------------ tests/fancy_pointer/sparse_hash_map_tests.cpp | 182 ----------------- tests/fancy_pointer/sparse_hash_set_tests.cpp | 193 ------------------ .../sparse_array_tests.cpp | 129 ------------ .../sparse_hash_set_tests.cpp | 51 ----- 9 files changed, 2 insertions(+), 770 deletions(-) delete mode 100644 include/tsl/boost_offset_pointer.h delete mode 100644 tests/fancy_pointer/CustomAllocator.h delete mode 100644 tests/fancy_pointer/sparse_array_tests.cpp delete mode 100644 tests/fancy_pointer/sparse_hash_map_tests.cpp delete mode 100644 tests/fancy_pointer/sparse_hash_set_tests.cpp delete mode 100644 tests/scoped_allocator_adaptor/sparse_array_tests.cpp delete mode 100644 tests/scoped_allocator_adaptor/sparse_hash_set_tests.cpp diff --git a/include/tsl/boost_offset_pointer.h b/include/tsl/boost_offset_pointer.h deleted file mode 100644 index a94afc1..0000000 --- a/include/tsl/boost_offset_pointer.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 fd9d64b..6a83f0a 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -178,8 +178,7 @@ inline int popcount(unsigned int x) { return fallback_popcount(x); } /* Replacement for const_cast in sparse_array. - * Can be overloaded for specific fancy pointers - * (see: include/tsl/boost_offset_pointer.h). + * Can be overloaded for specific fancy pointers. * This is just a workaround. * The clean way would be to change the implementation to stop using const_cast. */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 622d0b3..348f5c6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,14 +7,7 @@ add_executable(tsl_sparse_map_tests "main.cpp" "policy_tests.cpp" "popcount_tests.cpp" "sparse_map_tests.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" - "../include/tsl/boost_offset_pointer.h" - "scoped_allocator_adaptor/sparse_array_tests.cpp" - "scoped_allocator_adaptor/sparse_hash_set_tests.cpp") + "sparse_set_tests.cpp") target_compile_features(tsl_sparse_map_tests PRIVATE cxx_std_11) diff --git a/tests/fancy_pointer/CustomAllocator.h b/tests/fancy_pointer/CustomAllocator.h deleted file mode 100644 index 7340fef..0000000 --- a/tests/fancy_pointer/CustomAllocator.h +++ /dev/null @@ -1,46 +0,0 @@ -/** @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 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 { - using value_type = T; - template using offset_ptr = boost::interprocess::offset_ptr

; - using pointer = offset_ptr; - using const_pointer = offset_ptr; - using void_pointer = offset_ptr; - using const_void_pointer = offset_ptr; - using difference_type = typename offset_ptr::difference_type; - - OffsetAllocator() noexcept = default; - OffsetAllocator(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); - } -}; - -#endif //TSL_SPARSE_MAP_TESTS_CUSTOMALLOCATOR_H \ No newline at end of file diff --git a/tests/fancy_pointer/sparse_array_tests.cpp b/tests/fancy_pointer/sparse_array_tests.cpp deleted file mode 100644 index 6e590c9..0000000 --- a/tests/fancy_pointer/sparse_array_tests.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/** @file - * @brief Checks for fancy pointer support in the sparse_array implementation. - */ - -#include -#include -#include "CustomAllocator.h" - -// Globals -constexpr auto MAX_INDEX = 32; //BITMAP_NB_BITS - -/* 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; -} - -template -void construction() { - typename T::Allocator a; - typename T::Array test(MAX_INDEX, a); - test.clear(a); //needed because destructor asserts -} - -namespace details { - template - 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); - } - return arr; - } - - template - 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; - } - return check; - } -} - -template -void set() { - typename T::Allocator a; - auto test = details::generate_test_array(a); - auto check = details::generate_check_for_test_array(); - //'set' did not create the correct order of items - BOOST_REQUIRE(std::equal(test.begin(), test.end(), check.begin())); - 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(); - //'copy' changed the order of the items - BOOST_REQUIRE(std::equal(copy.begin(), copy.end(), check.begin())); - test.clear(a); - copy.clear(a); -} - -template -void move_construction() { - typename T::Allocator 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(); - //'move' changed the order of the items - BOOST_REQUIRE(std::equal(moved_to.begin(), moved_to.end(), check.begin())); - moved_to.clear(a); -} - -template -void const_iterator() { - typename T::Allocator a; - auto test = details::generate_test_array(a); - auto const_iter = test.cbegin(); - //const iterator has the wrong type - BOOST_REQUIRE((std::is_same::value)); - test.clear(a); -} - - -/* - * This are the types you can give the tests as template parameters. - */ -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; -}; - - - -/* 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>();} -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_map_tests.cpp b/tests/fancy_pointer/sparse_hash_map_tests.cpp deleted file mode 100644 index 087fadf..0000000 --- a/tests/fancy_pointer/sparse_hash_map_tests.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/** @file - * @brief Checks for fancy pointer support in the sparse_hash implementation for pair values (maps). - */ - -#include -#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; - const key_type &operator()(std::pair const &key_value) const noexcept { - return key_value.first; - } - 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; - } - 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 - typename T::Map 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 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 - 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 - 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 -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); - //'insert' did not create exactly the values needed - BOOST_REQUIRE(details::is_equal(map, l)); -} - -template -void iterator_insert(std::initializer_list l) { - auto map = details::default_construct_map(); - map.insert(l.begin(), l.end()); - //'insert' with iterators did not create exactly the values needed - BOOST_REQUIRE(details::is_equal(map, l)); -} - -template -void iterator_access(typename T::value_type single_value) { - auto map = details::default_construct_map(); - map.insert(single_value); - //iterator cannot access single value - BOOST_REQUIRE( (*(map.begin()) == 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()); - //iterating over the map didn't work - BOOST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), - map_sorted.begin())); -} - -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; - - //changing a single value didn't work - BOOST_REQUIRE(details::is_equal(map, check)); -} - - -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(fancy_pointers) -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(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; - 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; - }; - //size did not match - BOOST_REQUIRE(data.size() == map.size()); - //map did not contain all values - BOOST_REQUIRE(std::all_of(data.begin(), data.end(), check)); -} - -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 deleted file mode 100644 index 4560825..0000000 --- a/tests/fancy_pointer/sparse_hash_set_tests.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/** @file - * @brief Checks for fancy pointer support in the sparse_hash implementation for single values (sets). - */ - -#include -#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; - 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 - 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. - * 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](typename Set::value_type 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); - //'insert' did not create exactly the values needed - BOOST_REQUIRE(details::is_equal(set, l)); -} - -template -void iterator_insert(std::initializer_list l) { - auto set = details::default_construct_set(); - set.insert(l.begin(), l.end()); - //'insert' with iterators did not create exactly the values needed - BOOST_REQUIRE(details::is_equal(set, l)); -} - -template -void iterator_access(typename T::value_type single_value) { - auto set = details::default_construct_set(); - set.insert(single_value); - //iterator cannot access single value - BOOST_REQUIRE(*(set.begin()) == 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()); - //iterating over the set didn't work - BOOST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), - set_sorted.begin())); -} - - -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()); - //const iterating over the set didn't work - BOOST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), - set_sorted.begin())); -} - -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(); - //find did not work as expected - BOOST_REQUIRE((found == is_in_list)); -} - -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()); - // force non-const iterator - auto iter = set.begin(); - for(; *iter != extra_value; ++iter); - set.erase(iter); - //erase did not work as expected - BOOST_REQUIRE(details::is_equal(set, l)); -} - -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()); - //force const iterator - auto iter = set.cbegin(); - for(; *iter != extra_value; ++iter); - set.erase(iter); - //erase did not work as expected - BOOST_REQUIRE(details::is_equal(set, l)); -} - - -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(fancy_pointers) -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});} -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_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});} -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_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; - 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);}; - //size did not match - BOOST_REQUIRE(data.size() == set.size()); - //Set did not contain all values - BOOST_REQUIRE(std::all_of(data.begin(), data.end(), check)); -} - -BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/scoped_allocator_adaptor/sparse_array_tests.cpp b/tests/scoped_allocator_adaptor/sparse_array_tests.cpp deleted file mode 100644 index a612696..0000000 --- a/tests/scoped_allocator_adaptor/sparse_array_tests.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -// Globals -constexpr auto MAX_INDEX = 32; // BITMAP_NB_BITS - -template void compilation() { typename T::Array test; } - -template void construction() { - typename T::Allocator a; - typename T::Array test(MAX_INDEX, a); - test.clear(a); -} - -template -void set(std::initializer_list l) { - typename T::Allocator a; - typename T::Array array(MAX_INDEX, a); - std::vector check; - check.reserve(l.size()); - std::size_t counter = 0; - for (auto const &value : l) { - array.set(a, counter++, value); - check.emplace_back(value); - } - //'set' did not create the correct order of items - BOOST_REQUIRE(std::equal(array.begin(), array.end(), check.begin())); - array.clear(a); -} - -template void uses_allocator() { - //uses_allocator returns false - BOOST_REQUIRE((std::uses_allocator::value)); -} - -template -void trailing_allocator_convention(Args...) { - using Alloc = typename T::Allocator; - //trailing_allocator thinks construction is not possible - BOOST_REQUIRE((std::is_constructible::value)); -} - -template void trailing_allocator_convention_without_parameters() { - using Alloc = typename std::allocator_traits< - typename T::Allocator>::template rebind_alloc; - //trailing_allocator thinks construction is not possible - BOOST_REQUIRE((std::is_constructible::value)); -} - -template -void is_move_insertable(std::initializer_list l) { - using A = typename std::allocator_traits< - typename T::Allocator>::template rebind_alloc; - A m; - auto p = std::allocator_traits::allocate(m, 1); - typename T::Allocator ArrayAlloc; - typename T::Array rv(MAX_INDEX, ArrayAlloc); - std::size_t counter = 0; - for (auto const &value : l) { - rv.set(ArrayAlloc, counter++, value); - } - std::allocator_traits::construct(m, p, std::move(rv)); - rv.clear(ArrayAlloc); - p->clear(ArrayAlloc); - std::allocator_traits::destroy(m, p); - std::allocator_traits::deallocate(m, p, 1); -} - -template void is_default_insertable() { - using A = typename std::allocator_traits< - typename T::Allocator>::template rebind_alloc; - A m; - typename T::Array *p = std::allocator_traits::allocate(m, 1); - std::allocator_traits::construct(m, p); - std::allocator_traits::deallocate(m, p, 1); -} - -template -struct NORMAL { - using value_type = T; - using Allocator = std::allocator; - using Array = tsl::detail_sparse_hash::sparse_array; -}; - -template -struct SCOPED { - using value_type = T; - using Allocator = std::scoped_allocator_adaptor>; - using Array = tsl::detail_sparse_hash::sparse_array; -}; - -BOOST_AUTO_TEST_SUITE(scoped_allocators) -BOOST_AUTO_TEST_SUITE(sparse_array_tests) - -BOOST_AUTO_TEST_CASE(normal_compilation) { compilation>(); } -BOOST_AUTO_TEST_CASE(normal_construction) { construction>(); } -BOOST_AUTO_TEST_CASE(normal_set) { set>({0, 1, 2, 3, 4}); } -BOOST_AUTO_TEST_CASE(normal_uses_allocator) { uses_allocator>(); } -BOOST_AUTO_TEST_CASE(normal_trailing_allocator_convention) { - trailing_allocator_convention>(0); -} -BOOST_AUTO_TEST_CASE(normal_is_move_insertable) { - is_move_insertable>({0, 1, 2, 3, 4, 5}); -} -BOOST_AUTO_TEST_CASE(normal_is_default_insertable) { - is_default_insertable>(); -} - -BOOST_AUTO_TEST_CASE(scoped_compilation) { compilation>(); } -BOOST_AUTO_TEST_CASE(scoped_construction) { construction>(); } -BOOST_AUTO_TEST_CASE(scoped_set) { set>({0, 1, 2, 3, 4}); } -BOOST_AUTO_TEST_CASE(scoped_uses_allocator) { uses_allocator>(); } -BOOST_AUTO_TEST_CASE(scoped_trailing_allocator_convention) { - trailing_allocator_convention>(0); -} -BOOST_AUTO_TEST_CASE(scoped_is_move_insertable) { - is_move_insertable>({0, 1, 2, 3, 4, 5}); -} -BOOST_AUTO_TEST_CASE(scoped_is_default_insertable) { - is_default_insertable>(); -} - -BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/scoped_allocator_adaptor/sparse_hash_set_tests.cpp b/tests/scoped_allocator_adaptor/sparse_hash_set_tests.cpp deleted file mode 100644 index 4fbf569..0000000 --- a/tests/scoped_allocator_adaptor/sparse_hash_set_tests.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include - -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, details::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>; -} // namespace details - -template void construction() { - using Type = typename T::value_type; - typename T::Set(T::Set::DEFAULT_INIT_BUCKET_COUNT, std::hash(), - std::equal_to(), typename T::Allocator(), - T::Set::DEFAULT_MAX_LOAD_FACTOR); -} - - -template -struct NORMAL { - using value_type = T; - using Allocator = std::allocator; - using Set = details::sparse_set; -}; - -template -struct SCOPED { - using value_type = T; - using Allocator = std::scoped_allocator_adaptor>; - using Set = details::sparse_set; -}; - -BOOST_AUTO_TEST_SUITE(scoped_allocators) -BOOST_AUTO_TEST_SUITE(sparse_hash_set_tests) - -BOOST_AUTO_TEST_CASE(normal_construction){construction>();} - -BOOST_AUTO_TEST_CASE(scoped_construction){construction>();} - -BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 726a78b0ee0e75907dee3fd308e2ed7e5dc188ae Mon Sep 17 00:00:00 2001 From: Alexander Bigerl Date: Thu, 3 Jun 2021 20:53:09 +0200 Subject: [PATCH 6/6] reverted whitespace changes (#4) * reverted whitespace changes * added typename for sparse_hash::value_type/const_pointer --- include/tsl/sparse_hash.h | 4 ++-- tests/CMakeLists.txt | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index 6a83f0a..c9b5c88 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -1126,10 +1126,10 @@ class sparse_hash : private Allocator, public: using iterator_category = std::forward_iterator_tag; - using value_type = const sparse_hash::value_type; + using value_type = const typename sparse_hash::value_type; using difference_type = std::ptrdiff_t; using reference = value_type &; - using pointer = sparse_hash::const_pointer; + using pointer = typename sparse_hash::const_pointer; sparse_iterator() noexcept {} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 348f5c6..81383a3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,11 +3,11 @@ 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") + "custom_allocator_tests.cpp" + "policy_tests.cpp" + "popcount_tests.cpp" + "sparse_map_tests.cpp" + "sparse_set_tests.cpp") target_compile_features(tsl_sparse_map_tests PRIVATE cxx_std_11) @@ -23,4 +23,4 @@ target_link_libraries(tsl_sparse_map_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) \ No newline at end of file +target_link_libraries(tsl_sparse_map_tests PRIVATE tsl::sparse_map)