diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 698dff92..5d28e3a2 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1,5 +1,6 @@ # Copyright 2006-2008 Daniel James. +# Copyright 2022 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) @@ -77,6 +78,7 @@ test-suite unordered [ run unordered/contains_tests.cpp ] [ run unordered/mix_policy.cpp ] [ run unordered/erase_if.cpp ] + [ run unordered/scary_tests.cpp ] [ run unordered/compile_set.cpp : : : BOOST_UNORDERED_USE_MOVE diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 1479b5ab..0bcf78e8 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -500,11 +500,9 @@ namespace test { friend class const_ptr; friend struct void_ptr; - T* ptr_; - - ptr(T* x) : ptr_(x) {} - public: + T* ptr_; + ptr(T* x) : ptr_(x) {} ptr() : ptr_(0) {} explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {} diff --git a/test/unordered/scary_tests.cpp b/test/unordered/scary_tests.cpp new file mode 100644 index 00000000..f671f68e --- /dev/null +++ b/test/unordered/scary_tests.cpp @@ -0,0 +1,326 @@ +// Copyright 2022 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) + +// clang-format off +#include "../helpers/prefix.hpp" +#include +#include +#include "../helpers/postfix.hpp" +// clang-format on + +#include "../helpers/test.hpp" +#include "../objects/test.hpp" + +#include + +struct hash1 +{ + template std::size_t operator()(Key const&) const { return 1337; } +}; + +struct hash2 +{ + template std::size_t operator()(Key const&) const { return 7331; } +}; + +struct equal1 +{ + template bool operator==(T const&) const { return true; } +}; + +struct equal2 +{ + template bool operator==(T const&) const { return true; } +}; + +/////////////////////////////////////////////////////////////////////////////// +// we define two duplicated allocators here, each one having the same smart +// pointer type +// we want to prove in our test that different allocators with the same pointers +// (either fancy or raw) work equally well for our SCARY iterators +// +template struct allocator1 +{ +#ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS +public: +#else + template friend struct allocator1; +#endif + +public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef test::void_ptr void_pointer; + typedef test::void_const_ptr const_void_pointer; + typedef test::ptr pointer; + typedef test::const_ptr const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind + { + typedef allocator1 other; + }; + + allocator1() {} + + template allocator1(allocator1 const&) {} + + allocator1(allocator1 const&) {} + + ~allocator1() {} + + pointer address(reference r) { return pointer(&r); } + + const_pointer address(const_reference r) { return const_pointer(&r); } + + pointer allocate(size_type n) + { + pointer p(static_cast(::operator new(n * sizeof(T)))); + return p; + } + + pointer allocate(size_type n, void const*) + { + pointer ptr(static_cast(::operator new(n * sizeof(T)))); + return ptr; + } + + void deallocate(pointer p, size_type) + { + ::operator delete((void*)p.operator->()); + } + +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + template void construct(U* p, V const& v) { new (p) U(v); } +#else + template + void construct(U* p, BOOST_FWD_REF(Args)... args) + { + new (p) U(boost::forward(args)...); + } +#endif + + // msvc-12.0 and msvc-14.0 seem to eliminate the destructor call as we're only + // ever using it with an int with has a trivial destructor so it eliminates + // the code and generates a warning about an unused variable + // + template + void destroy(U* p) + { + (void)p; + p->~U(); + } + + size_type max_size() const { return (std::numeric_limits::max)(); } + + bool operator==(allocator1 const&) const { return true; } + bool operator!=(allocator1 const&) const { return false; } + + enum + { + is_select_on_copy = false, + is_propagate_on_swap = false, + is_propagate_on_assign = false, + is_propagate_on_move = false + }; +}; + +template struct allocator2 +{ +#ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS +public: +#else + template friend struct allocator2; +#endif + +public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef test::void_ptr void_pointer; + typedef test::void_const_ptr const_void_pointer; + typedef test::ptr pointer; + typedef test::const_ptr const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind + { + typedef allocator2 other; + }; + + allocator2() {} + + template allocator2(allocator2 const&) {} + + allocator2(allocator2 const&) {} + + ~allocator2() {} + + pointer address(reference r) { return pointer(&r); } + + const_pointer address(const_reference r) { return const_pointer(&r); } + + pointer allocate(size_type n) + { + pointer p(static_cast(::operator new(n * sizeof(T)))); + return p; + } + + pointer allocate(size_type n, void const*) + { + pointer ptr(static_cast(::operator new(n * sizeof(T)))); + return ptr; + } + + void deallocate(pointer p, size_type) { ::operator delete((void*)p.ptr_); } + +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + template void construct(U* p, V const& v) { new (p) U(v); } +#else + template + void construct(U* p, BOOST_FWD_REF(Args)... args) + { + new (p) U(boost::forward(args)...); + } +#endif + + // msvc-12.0 and msvc-14.0 seem to eliminate the destructor call as we're only + // ever using it with an int with has a trivial destructor so it eliminates + // the code and generates a warning about an unused variable + // + template + void destroy(U* p) + { + (void)p; + p->~U(); + } + + size_type max_size() const { return (std::numeric_limits::max)(); } + + bool operator==(allocator2 const&) const { return true; } + bool operator!=(allocator2 const&) const { return false; } + + enum + { + is_select_on_copy = false, + is_propagate_on_swap = false, + is_propagate_on_assign = false, + is_propagate_on_move = false + }; +}; + +template void scary_test() +{ + C1 x; + C2 y; + + typename C2::iterator begin(x.begin()); + BOOST_TEST(begin == x.end()); + + typename C2::const_iterator cbegin(x.cbegin()); + BOOST_TEST(cbegin == x.cend()); + + BOOST_ASSERT(x.bucket_count() > 0); + + typename C2::local_iterator lbegin(x.begin(0)); + BOOST_TEST(lbegin == x.end(0)); + + typename C2::const_local_iterator clbegin(x.cbegin(0)); + BOOST_TEST(clbegin == x.cend(0)); +} + +template < + template + class Map> +void map_scary_test() +{ + typedef std::pair value_type; + typedef std::allocator std_allocator_type; + + typedef Map, std_allocator_type> + hash1_unordered_map; + typedef Map, std_allocator_type> + hash2_unordered_map; + + typedef Map, equal1, std_allocator_type> + equal1_unordered_map; + typedef Map, equal2, std_allocator_type> + equal2_unordered_map; + + // test allocators with a raw pointer as their `::pointer` type + // + typedef Map, std::equal_to, + test::allocator1 > + alloc1_unordered_map; + typedef Map, std::equal_to, + std_allocator_type> + std_alloc_unordered_map; + + // test allocators with a fancy pointer as their `::pointer` type + // + typedef Map, std::equal_to, + allocator1 > + fancy1_unordered_map; + typedef Map, std::equal_to, + allocator2 > + fancy2_unordered_map; + + scary_test(); + scary_test(); + scary_test(); + scary_test(); +} + +template