From 0ff1fa0f6e33b4add4361dc181edde6b183fe47f Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 23 Feb 2023 11:05:38 -0800 Subject: [PATCH] Add test that ensures proper uses-allocator construction is followed --- test/Jamfile.v2 | 1 + test/unordered/uses_allocator.cpp | 189 ++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 test/unordered/uses_allocator.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 4f7a537f..31b17efd 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -144,6 +144,7 @@ build_foa init_type_insert_tests ; build_foa max_load_tests ; build_foa extract_tests ; build_foa node_handle_tests ; +build_foa uses_allocator ; run unordered/hash_is_avalanching_test.cpp ; diff --git a/test/unordered/uses_allocator.cpp b/test/unordered/uses_allocator.cpp new file mode 100644 index 00000000..066a3585 --- /dev/null +++ b/test/unordered/uses_allocator.cpp @@ -0,0 +1,189 @@ + +// Copyright 2023 Christian Mazakas. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include "../helpers/test.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#if BOOST_CXX_VERSION <= 199711L || \ + BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40800) || \ + (defined(BOOST_LIBSTDCXX_VERSION) && BOOST_CXX_VERSION > 201703L) || \ + (defined(BOOST_MSVC_FULL_VER) && BOOST_MSVC_FULL_VER >= 192000000 && \ + BOOST_MSVC_FULL_VER < 193000000) + +// automatically disable this test for C++03 builds so we can use the STL's +// scoped_allocator_adaptor +// we remove C++20 support for libstdc++ builds because of: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108952 +// +// msvc-14.2 w/ C++20 is similarly affected +// + + +BOOST_PRAGMA_MESSAGE("uses_allocator tests require C++11, scoped_allocator") + +int main() {} + +#else + +#include +#include +#include +#include + +template struct allocator +{ + typedef T value_type; + + int tag_ = -1; + + allocator() = default; + allocator(int tag) : tag_{tag} {} + allocator(allocator const&) = default; + allocator(allocator&&) = default; + + template allocator(allocator const& rhs) : tag_{rhs.tag_} {} + + BOOST_ATTRIBUTE_NODISCARD T* allocate(std::size_t n) + { + return static_cast(::operator new(n * sizeof(T))); + } + + void deallocate(T* p, std::size_t) noexcept { ::operator delete(p); } + + allocator& operator=(allocator const& rhs) + { + tag_ = rhs.tag_; + return *this; + } + + allocator& operator=(allocator&& rhs) noexcept + { + tag_ = rhs.tag_; + return *this; + } + + bool operator==(allocator const&) const { return true; } + bool operator!=(allocator const&) const { return false; } +}; + +struct raii_tracker +{ + static int count; + static int copy_count; + static int move_count; + static int alloc_move_count; + + using allocator_type = allocator; + + allocator_type a_; + + raii_tracker(allocator_type a) : a_(a) { ++count; } + raii_tracker(int, allocator_type const& a) : a_(a) { ++count; } + + raii_tracker(raii_tracker const&) { ++copy_count; } + raii_tracker(raii_tracker&&) noexcept { ++move_count; } + raii_tracker(raii_tracker&&, allocator_type const& a) noexcept : a_(a) + { + ++alloc_move_count; + } + + allocator_type get_allocator() const noexcept { return a_; } + + friend bool operator==(raii_tracker const&, raii_tracker const&) + { + return true; + } +}; + +int raii_tracker::count = 0; +int raii_tracker::copy_count = 0; +int raii_tracker::move_count = 0; +int raii_tracker::alloc_move_count = 0; + +static void reset_counts() +{ + raii_tracker::count = 0; + raii_tracker::copy_count = 0; + raii_tracker::move_count = 0; + raii_tracker::alloc_move_count = 0; +} + +std::size_t hash_value(raii_tracker const&) { return 0; } + +using map_allocator_type = std::scoped_allocator_adaptor< + allocator >, allocator >; + +using set_allocator_type = + std::scoped_allocator_adaptor, allocator >; + +using map_type = boost::unordered_flat_map, std::equal_to, map_allocator_type>; + +using node_map_type = boost::unordered_node_map, std::equal_to, map_allocator_type>; + +using set_type = boost::unordered_flat_set, std::equal_to, set_allocator_type>; + +using node_set_type = boost::unordered_node_set, std::equal_to, set_allocator_type>; + +map_type* flat_map; +node_map_type* node_map; + +set_type* flat_set; +node_set_type* node_set; + +template static void map_uses_allocator_construction(X*) +{ + reset_counts(); + + map_allocator_type alloc( + allocator >{12}, + allocator{34}); + + X map(1, alloc); + map.emplace( + std::piecewise_construct, std::make_tuple(1337), std::make_tuple(7331)); + + BOOST_TEST_EQ(raii_tracker::count, 2); + BOOST_TEST_EQ(raii_tracker::move_count, 0); + BOOST_TEST_EQ(raii_tracker::alloc_move_count, 2); + + BOOST_TEST_EQ(map.begin()->first.get_allocator().tag_, 34); + BOOST_TEST_EQ(map.begin()->second.get_allocator().tag_, 34); +} + +template static void set_uses_allocator_construction(X*) +{ + reset_counts(); + + set_allocator_type alloc(allocator{12}, allocator{34}); + + X set(1, alloc); + set.emplace(); + + BOOST_TEST_EQ(raii_tracker::count, 1); + BOOST_TEST_EQ(raii_tracker::move_count, 0); + BOOST_TEST_EQ(raii_tracker::alloc_move_count, 1); + + BOOST_TEST_EQ(set.begin()->get_allocator().tag_, 34); +} + +UNORDERED_TEST(map_uses_allocator_construction, ((flat_map)(node_map))) +UNORDERED_TEST(set_uses_allocator_construction, ((flat_set)(node_set))) + +RUN_TESTS() + +#endif