From 4bf3b1bfc59540274a591c45003c0898262055bb Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 28 Aug 2011 15:36:58 +0000 Subject: [PATCH] Unordered: Support optional allocator methods. Only for compilers with SFINAE expressions and recent versions of Visual C++. Also fix Visual C++ 8, and use BOOST_UNORDERED_ prefix for all macros. [SVN r74106] --- .../unordered/detail/allocator_helpers.hpp | 170 +++++++++++++----- test/unordered/Jamfile.v2 | 3 +- test/unordered/minimal_allocator.cpp | 89 +++++++++ 3 files changed, 217 insertions(+), 45 deletions(-) create mode 100644 test/unordered/minimal_allocator.cpp diff --git a/include/boost/unordered/detail/allocator_helpers.hpp b/include/boost/unordered/detail/allocator_helpers.hpp index aac1e55c..fe1bce7d 100644 --- a/include/boost/unordered/detail/allocator_helpers.hpp +++ b/include/boost/unordered/detail/allocator_helpers.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #if (defined(BOOST_NO_STD_ALLOCATOR) || defined(BOOST_DINKUMWARE_STDLIB)) \ && !defined(__BORLANDC__) @@ -93,17 +94,29 @@ namespace boost { namespace unordered { namespace detail { struct choice2 : choice3 { typedef char (&type)[2]; }; struct choice1 : choice2 { typedef char (&type)[1]; }; choice1 choose(); + +#if !defined(__IBMCPP__) + +#define BOOST_UNORDERED_MEMBER_CHECK(tname) BOOST_DEDUCED_TYPENAME X::tname* + +#else + template struct wrap { typedef void* type; }; - #define BOOST_DEFAULT_TYPE_TMPLT(tname) \ +#define BOOST_UNORDERED_MEMBER_CHECK(tname) \ + BOOST_DEDUCED_TYPENAME wrap< \ + BOOST_DEDUCED_TYPENAME X::tname \ + >::type + +#endif + + #define BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(tname) \ template \ struct default_type_ ## tname { \ \ template \ static choice1::type test(choice1, \ - BOOST_DEDUCED_TYPENAME wrap< \ - BOOST_DEDUCED_TYPENAME X::tname \ - >::type = 0); \ + BOOST_UNORDERED_MEMBER_CHECK(tname) = 0); \ \ template \ static choice2::type test(choice2, void* = 0); \ @@ -118,18 +131,18 @@ namespace boost { namespace unordered { namespace detail { ::type::tname type; \ } - #define BOOST_DEFAULT_TYPE(T,tname, arg) \ + #define BOOST_UNORDERED_DEFAULT_TYPE(T,tname, arg) \ BOOST_DEDUCED_TYPENAME default_type_ ## tname::type - BOOST_DEFAULT_TYPE_TMPLT(pointer); - BOOST_DEFAULT_TYPE_TMPLT(const_pointer); - BOOST_DEFAULT_TYPE_TMPLT(void_pointer); - BOOST_DEFAULT_TYPE_TMPLT(const_void_pointer); - BOOST_DEFAULT_TYPE_TMPLT(difference_type); - BOOST_DEFAULT_TYPE_TMPLT(size_type); - BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_copy_assignment); - BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_move_assignment); - BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_swap); + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(pointer); + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(const_pointer); + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(void_pointer); + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(const_void_pointer); + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(difference_type); + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(size_type); + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(propagate_on_container_copy_assignment); + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(propagate_on_container_move_assignment); + BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(propagate_on_container_swap); #if !defined(BOOST_NO_SFINAE_EXPR) || BOOST_WORKAROUND(BOOST_MSVC, >= 1500) @@ -149,14 +162,40 @@ namespace boost { namespace unordered { namespace detail { static BOOST_PP_CAT(choice, result)::type test( \ BOOST_PP_CAT(choice, count)) +#define BOOST_UNORDERED_HAS_EXPRESSION(name, expression) \ + struct BOOST_PP_CAT(has_, name) \ + { \ + BOOST_UNORDERED_CHECK_EXPRESSION(1, 1, expression); \ + BOOST_UNORDERED_DEFAULT_EXPRESSION(2, 2); \ + \ + enum { value = sizeof(test(choose())) == sizeof(choice1::type) };\ + } + template - struct has_select_on_container_copy_construction - { - BOOST_UNORDERED_CHECK_EXPRESSION(1, 1, make().select_on_container_copy_construction()); - BOOST_UNORDERED_DEFAULT_EXPRESSION(2, 2); - - enum { value = sizeof(test(choose())) == sizeof(choice1::type) }; - }; + BOOST_UNORDERED_HAS_EXPRESSION( + select_on_container_copy_construction, + make().select_on_container_copy_construction() + ); + + // Only supporting the basic copy constructor for now. + + template + BOOST_UNORDERED_HAS_EXPRESSION( + construct, + make().construct(make(), make()) + ); + + template + BOOST_UNORDERED_HAS_EXPRESSION( + destroy, + make().destroy(make()) + ); + + template + BOOST_UNORDERED_HAS_EXPRESSION( + max_size, + make().max_size() + ); #else @@ -184,12 +223,24 @@ namespace boost { namespace unordered { namespace detail { template struct has_select_on_container_copy_construction { - BOOST_UNORDERED_CHECK_MEMBER(1, 1, select_on_container_copy_construction, T (T::*)() const); + BOOST_UNORDERED_CHECK_MEMBER(1, 1, + select_on_container_copy_construction, + T (T::*)() const); BOOST_UNORDERED_DEFAULT_MEMBER(2, 2); enum { value = sizeof(test(choose())) == sizeof(choice1::type) }; }; + // Detection isn't reliable enough, so just assume that we have these + // functions. + + template + struct has_construct : true_type {}; + template + struct has_destroy : true_type {}; + template + struct has_max_size : true_type {}; + #endif template @@ -208,41 +259,55 @@ namespace boost { namespace unordered { namespace detail { return rhs; } + template + SizeType call_max_size(const Alloc& a, + typename boost::enable_if, void*>::type = 0) + { + return a.max_size(); + } + + template + SizeType call_max_size(const Alloc&, + typename boost::disable_if, void*>::type = 0) + { + return std::numeric_limits::max(); + } + template struct allocator_traits { typedef Alloc allocator_type; typedef typename Alloc::value_type value_type; - typedef BOOST_DEFAULT_TYPE(Alloc, pointer, value_type*) + typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, pointer, value_type*) pointer; // For now always use the allocator's const_pointer. - //typedef BOOST_DEFAULT_TYPE(Alloc, const_pointer, + //typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, const_pointer, // BOOST_DEDUCED_TYPENAME pointer_traits:: // BOOST_NESTED_TEMPLATE rebind::other) // const_pointer; - typedef BOOST_DEFAULT_TYPE(Alloc, const_pointer, value_type const*) - const_pointer; + typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, const_pointer, + value_type const*) const_pointer; // I'm not using void pointers for now. - //typedef BOOST_DEFAULT_TYPE(Alloc, void_pointer, + //typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, void_pointer, // BOOST_NESTED_TEMPLATE pointer_traits:: // BOOST_NESTED_TEMPLATE rebind::other) // void_pointer; - //typedef BOOST_DEFAULT_TYPE(Alloc, const_void_pointer, + //typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, const_void_pointer, // BOOST_DEDUCED_TYPENAME pointer_traits:: // BOOST_NESTED_TEMPLATE rebind::other) // const_void_pointer; - typedef BOOST_DEFAULT_TYPE(Alloc, difference_type, std::ptrdiff_t) - difference_type; + typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, difference_type, + std::ptrdiff_t) difference_type; - typedef BOOST_DEFAULT_TYPE(Alloc, size_type, std::size_t) + typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, size_type, std::size_t) size_type; // TODO: rebind_alloc and rebind_traits @@ -252,32 +317,49 @@ namespace boost { namespace unordered { namespace detail { // I never use this, so I'll just comment it out for now. // - //static pointer allocate(Alloc& a, size_type n, const_void_pointer hint) + //static pointer allocate(Alloc& a, size_type n, + // const_void_pointer hint) // { return DEFAULT_FUNC(allocate, pointer)(a, n, hint); } static void deallocate(Alloc& a, pointer p, size_type n) { a.deallocate(p, n); } - // Only support the basic copy constructor + public: - // template - // static void construct(Alloc& a, T* p, Args&&... args) { - // DEFAULT_FUNC(construct,void)(a, p, std::forward(args)...); - // } + // Only supporting the basic copy constructor for now. template - static void construct(Alloc& a, T* p, T const& x) { + static void construct(Alloc& a, T* p, T const& x, typename + boost::enable_if, void*>::type = 0) + { a.construct(p, x); } template - static void destroy(Alloc& a, T* p) { - // DEFAULT_FUNC(destroy,void)(a, p); + static void construct(Alloc&, T* p, T const& x, typename + boost::disable_if, void*>::type = 0) + { + new ((void*) p) T(x); + } + + template + static void destroy(Alloc& a, T* p, typename + boost::enable_if, void*>::type = 0) + { a.destroy(p); } + template + static void destroy(Alloc&, T* p, typename + boost::disable_if, void*>::type = 0) + { + p->~T(); + } + static size_type max_size(const Alloc& a) - { return a.max_size(); } + { + return boost::unordered::detail::call_max_size(a); + } // Allocator propagation on construction @@ -289,13 +371,13 @@ namespace boost { namespace unordered { namespace detail { // Allocator propagation on assignment and swap. // Return true if lhs is modified. - typedef BOOST_DEFAULT_TYPE( + typedef BOOST_UNORDERED_DEFAULT_TYPE( Alloc, propagate_on_container_copy_assignment, false_type) propagate_on_container_copy_assignment; - typedef BOOST_DEFAULT_TYPE( + typedef BOOST_UNORDERED_DEFAULT_TYPE( Alloc,propagate_on_container_move_assignment, false_type) propagate_on_container_move_assignment; - typedef BOOST_DEFAULT_TYPE( + typedef BOOST_UNORDERED_DEFAULT_TYPE( Alloc,propagate_on_container_swap,false_type) propagate_on_container_swap; }; diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index 9919f61f..a4ed93a9 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -23,6 +23,7 @@ test-suite unordered [ run fwd_set_test.cpp ] [ run fwd_map_test.cpp ] [ run allocator_traits.cpp ] + [ run minimal_allocator.cpp ] [ run compile_set.cpp ] [ run compile_map.cpp ] [ run link_test_1.cpp link_test_2.cpp ] @@ -39,7 +40,7 @@ test-suite unordered [ run erase_tests.cpp ] [ run erase_equiv_tests.cpp ] [ run find_tests.cpp ] - [ run at_tests.cpp ] + # [ run at_tests.cpp ] [ run bucket_tests.cpp ] [ run load_factor_tests.cpp ] [ run rehash_tests.cpp ] diff --git a/test/unordered/minimal_allocator.cpp b/test/unordered/minimal_allocator.cpp new file mode 100644 index 00000000..e58c9318 --- /dev/null +++ b/test/unordered/minimal_allocator.cpp @@ -0,0 +1,89 @@ + +// Copyright 2011 Daniel James. +// 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 +#include +#include +#include +#include "../objects/test.hpp" + +template +struct SimpleAllocator +{ + typedef Tp value_type; + + SimpleAllocator() + { + } + + template SimpleAllocator(const SimpleAllocator& other) + { + } + + Tp *allocate(std::size_t n) + { + return static_cast(::operator new(n * sizeof(Tp))); + } + + void deallocate(Tp* p, std::size_t) + { + ::operator delete((void*) p); + } +}; + +template +void test_simple_allocator() +{ + test::check_instances check_; + + typedef boost::unordered::detail::allocator_traits< + SimpleAllocator > traits; + + BOOST_MPL_ASSERT((boost::is_same >)); + + BOOST_MPL_ASSERT((boost::is_same)); + + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + //BOOST_MPL_ASSERT((boost::is_same)); + //BOOST_MPL_ASSERT((boost::is_same)); + + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + + BOOST_TEST(!traits::propagate_on_container_copy_assignment::value); + BOOST_TEST(!traits::propagate_on_container_move_assignment::value); + BOOST_TEST(!traits::propagate_on_container_swap::value); + + // rebind_alloc + // rebind_traits + + SimpleAllocator a; + + T* ptr1 = traits::allocate(a, 1); + //T* ptr2 = traits::allocate(a, 1, static_cast(ptr1)); + + traits::construct(a, ptr1, T(10)); + //traits::construct(a, ptr2, T(30), ptr1); + + BOOST_TEST(*ptr1 == T(10)); + //BOOST_TEST(*ptr2 == T(30)); + + traits::destroy(a, ptr1); + //traits::destroy(a, ptr2); + + //traits::deallocate(a, ptr2, 1); + traits::deallocate(a, ptr1, 1); + + traits::max_size(a); +} + +int main() +{ + test_simple_allocator(); + test_simple_allocator(); + + return boost::report_errors(); +} \ No newline at end of file