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]
This commit is contained in:
Daniel James
2011-08-28 15:36:58 +00:00
parent 6bc99ac0c2
commit 4bf3b1bfc5
3 changed files with 217 additions and 45 deletions

View File

@ -19,6 +19,7 @@
#include <boost/detail/select_type.hpp> #include <boost/detail/select_type.hpp>
#include <boost/utility/enable_if.hpp> #include <boost/utility/enable_if.hpp>
#include <boost/preprocessor/cat.hpp> #include <boost/preprocessor/cat.hpp>
#include <boost/limits.hpp>
#if (defined(BOOST_NO_STD_ALLOCATOR) || defined(BOOST_DINKUMWARE_STDLIB)) \ #if (defined(BOOST_NO_STD_ALLOCATOR) || defined(BOOST_DINKUMWARE_STDLIB)) \
&& !defined(__BORLANDC__) && !defined(__BORLANDC__)
@ -93,17 +94,29 @@ namespace boost { namespace unordered { namespace detail {
struct choice2 : choice3 { typedef char (&type)[2]; }; struct choice2 : choice3 { typedef char (&type)[2]; };
struct choice1 : choice2 { typedef char (&type)[1]; }; struct choice1 : choice2 { typedef char (&type)[1]; };
choice1 choose(); choice1 choose();
#if !defined(__IBMCPP__)
#define BOOST_UNORDERED_MEMBER_CHECK(tname) BOOST_DEDUCED_TYPENAME X::tname*
#else
template <typename T> struct wrap { typedef void* type; }; template <typename T> 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 <typename Tp, typename Default> \ template <typename Tp, typename Default> \
struct default_type_ ## tname { \ struct default_type_ ## tname { \
\ \
template <typename X> \ template <typename X> \
static choice1::type test(choice1, \ static choice1::type test(choice1, \
BOOST_DEDUCED_TYPENAME wrap< \ BOOST_UNORDERED_MEMBER_CHECK(tname) = 0); \
BOOST_DEDUCED_TYPENAME X::tname \
>::type = 0); \
\ \
template <typename X> \ template <typename X> \
static choice2::type test(choice2, void* = 0); \ static choice2::type test(choice2, void* = 0); \
@ -118,18 +131,18 @@ namespace boost { namespace unordered { namespace detail {
::type::tname type; \ ::type::tname type; \
} }
#define BOOST_DEFAULT_TYPE(T,tname, arg) \ #define BOOST_UNORDERED_DEFAULT_TYPE(T,tname, arg) \
BOOST_DEDUCED_TYPENAME default_type_ ## tname<T, arg>::type BOOST_DEDUCED_TYPENAME default_type_ ## tname<T, arg>::type
BOOST_DEFAULT_TYPE_TMPLT(pointer); BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(pointer);
BOOST_DEFAULT_TYPE_TMPLT(const_pointer); BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(const_pointer);
BOOST_DEFAULT_TYPE_TMPLT(void_pointer); BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(void_pointer);
BOOST_DEFAULT_TYPE_TMPLT(const_void_pointer); BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(const_void_pointer);
BOOST_DEFAULT_TYPE_TMPLT(difference_type); BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(difference_type);
BOOST_DEFAULT_TYPE_TMPLT(size_type); BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(size_type);
BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_copy_assignment); BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(propagate_on_container_copy_assignment);
BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_move_assignment); BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(propagate_on_container_move_assignment);
BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_swap); BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(propagate_on_container_swap);
#if !defined(BOOST_NO_SFINAE_EXPR) || BOOST_WORKAROUND(BOOST_MSVC, >= 1500) #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( \ static BOOST_PP_CAT(choice, result)::type test( \
BOOST_PP_CAT(choice, count)) 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<T>(choose())) == sizeof(choice1::type) };\
}
template <typename T> template <typename T>
struct has_select_on_container_copy_construction BOOST_UNORDERED_HAS_EXPRESSION(
{ select_on_container_copy_construction,
BOOST_UNORDERED_CHECK_EXPRESSION(1, 1, make<U const>().select_on_container_copy_construction()); make<U const>().select_on_container_copy_construction()
BOOST_UNORDERED_DEFAULT_EXPRESSION(2, 2); );
enum { value = sizeof(test<T>(choose())) == sizeof(choice1::type) }; // Only supporting the basic copy constructor for now.
};
template <typename T, typename ValueType>
BOOST_UNORDERED_HAS_EXPRESSION(
construct,
make<U>().construct(make<ValueType*>(), make<ValueType const>())
);
template <typename T, typename ValueType>
BOOST_UNORDERED_HAS_EXPRESSION(
destroy,
make<U>().destroy(make<ValueType*>())
);
template <typename T>
BOOST_UNORDERED_HAS_EXPRESSION(
max_size,
make<U const>().max_size()
);
#else #else
@ -184,12 +223,24 @@ namespace boost { namespace unordered { namespace detail {
template <typename T> template <typename T>
struct has_select_on_container_copy_construction 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); BOOST_UNORDERED_DEFAULT_MEMBER(2, 2);
enum { value = sizeof(test<T>(choose())) == sizeof(choice1::type) }; enum { value = sizeof(test<T>(choose())) == sizeof(choice1::type) };
}; };
// Detection isn't reliable enough, so just assume that we have these
// functions.
template <typename Alloc, typename value_type>
struct has_construct : true_type {};
template <typename Alloc, typename value_type>
struct has_destroy : true_type {};
template <typename Alloc>
struct has_max_size : true_type {};
#endif #endif
template <typename Alloc> template <typename Alloc>
@ -208,41 +259,55 @@ namespace boost { namespace unordered { namespace detail {
return rhs; return rhs;
} }
template <typename SizeType, typename Alloc>
SizeType call_max_size(const Alloc& a,
typename boost::enable_if<has_max_size<Alloc>, void*>::type = 0)
{
return a.max_size();
}
template <typename SizeType, typename Alloc>
SizeType call_max_size(const Alloc&,
typename boost::disable_if<has_max_size<Alloc>, void*>::type = 0)
{
return std::numeric_limits<SizeType>::max();
}
template <typename Alloc> template <typename Alloc>
struct allocator_traits struct allocator_traits
{ {
typedef Alloc allocator_type; typedef Alloc allocator_type;
typedef typename Alloc::value_type value_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; pointer;
// For now always use the allocator's const_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<pointer>:: // BOOST_DEDUCED_TYPENAME pointer_traits<pointer>::
// BOOST_NESTED_TEMPLATE rebind<const value_type>::other) // BOOST_NESTED_TEMPLATE rebind<const value_type>::other)
// const_pointer; // const_pointer;
typedef BOOST_DEFAULT_TYPE(Alloc, const_pointer, value_type const*) typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, const_pointer,
const_pointer; value_type const*) const_pointer;
// I'm not using void pointers for now. // 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<pointer>:: // BOOST_NESTED_TEMPLATE pointer_traits<pointer>::
// BOOST_NESTED_TEMPLATE rebind<void>::other) // BOOST_NESTED_TEMPLATE rebind<void>::other)
// void_pointer; // void_pointer;
//typedef BOOST_DEFAULT_TYPE(Alloc, const_void_pointer, //typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, const_void_pointer,
// BOOST_DEDUCED_TYPENAME pointer_traits<pointer>:: // BOOST_DEDUCED_TYPENAME pointer_traits<pointer>::
// BOOST_NESTED_TEMPLATE rebind<const void>::other) // BOOST_NESTED_TEMPLATE rebind<const void>::other)
// const_void_pointer; // const_void_pointer;
typedef BOOST_DEFAULT_TYPE(Alloc, difference_type, std::ptrdiff_t) typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, difference_type,
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; size_type;
// TODO: rebind_alloc and rebind_traits // 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. // 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); } // { return DEFAULT_FUNC(allocate, pointer)(a, n, hint); }
static void deallocate(Alloc& a, pointer p, size_type n) static void deallocate(Alloc& a, pointer p, size_type n)
{ a.deallocate(p, n); } { a.deallocate(p, n); }
// Only support the basic copy constructor public:
// template <typename T, typename... Args> // Only supporting the basic copy constructor for now.
// static void construct(Alloc& a, T* p, Args&&... args) {
// DEFAULT_FUNC(construct,void)(a, p, std::forward<Args>(args)...);
// }
template <typename T> template <typename T>
static void construct(Alloc& a, T* p, T const& x) { static void construct(Alloc& a, T* p, T const& x, typename
boost::enable_if<has_construct<Alloc, T>, void*>::type = 0)
{
a.construct(p, x); a.construct(p, x);
} }
template <typename T> template <typename T>
static void destroy(Alloc& a, T* p) { static void construct(Alloc&, T* p, T const& x, typename
// DEFAULT_FUNC(destroy,void)(a, p); boost::disable_if<has_construct<Alloc, T>, void*>::type = 0)
{
new ((void*) p) T(x);
}
template <typename T>
static void destroy(Alloc& a, T* p, typename
boost::enable_if<has_destroy<Alloc, T>, void*>::type = 0)
{
a.destroy(p); a.destroy(p);
} }
template <typename T>
static void destroy(Alloc&, T* p, typename
boost::disable_if<has_destroy<Alloc, T>, void*>::type = 0)
{
p->~T();
}
static size_type max_size(const Alloc& a) static size_type max_size(const Alloc& a)
{ return a.max_size(); } {
return boost::unordered::detail::call_max_size<size_type>(a);
}
// Allocator propagation on construction // Allocator propagation on construction
@ -289,13 +371,13 @@ namespace boost { namespace unordered { namespace detail {
// Allocator propagation on assignment and swap. // Allocator propagation on assignment and swap.
// Return true if lhs is modified. // Return true if lhs is modified.
typedef BOOST_DEFAULT_TYPE( typedef BOOST_UNORDERED_DEFAULT_TYPE(
Alloc, propagate_on_container_copy_assignment, false_type) Alloc, propagate_on_container_copy_assignment, false_type)
propagate_on_container_copy_assignment; propagate_on_container_copy_assignment;
typedef BOOST_DEFAULT_TYPE( typedef BOOST_UNORDERED_DEFAULT_TYPE(
Alloc,propagate_on_container_move_assignment, false_type) Alloc,propagate_on_container_move_assignment, false_type)
propagate_on_container_move_assignment; propagate_on_container_move_assignment;
typedef BOOST_DEFAULT_TYPE( typedef BOOST_UNORDERED_DEFAULT_TYPE(
Alloc,propagate_on_container_swap,false_type) Alloc,propagate_on_container_swap,false_type)
propagate_on_container_swap; propagate_on_container_swap;
}; };

View File

@ -23,6 +23,7 @@ test-suite unordered
[ run fwd_set_test.cpp ] [ run fwd_set_test.cpp ]
[ run fwd_map_test.cpp ] [ run fwd_map_test.cpp ]
[ run allocator_traits.cpp ] [ run allocator_traits.cpp ]
[ run minimal_allocator.cpp ]
[ run compile_set.cpp ] [ run compile_set.cpp ]
[ run compile_map.cpp ] [ run compile_map.cpp ]
[ run link_test_1.cpp link_test_2.cpp ] [ run link_test_1.cpp link_test_2.cpp ]
@ -39,7 +40,7 @@ test-suite unordered
[ run erase_tests.cpp ] [ run erase_tests.cpp ]
[ run erase_equiv_tests.cpp ] [ run erase_equiv_tests.cpp ]
[ run find_tests.cpp ] [ run find_tests.cpp ]
[ run at_tests.cpp ] # [ run at_tests.cpp ]
[ run bucket_tests.cpp ] [ run bucket_tests.cpp ]
[ run load_factor_tests.cpp ] [ run load_factor_tests.cpp ]
[ run rehash_tests.cpp ] [ run rehash_tests.cpp ]

View File

@ -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 <boost/unordered/detail/allocator_helpers.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/assert.hpp>
#include "../objects/test.hpp"
template <class Tp>
struct SimpleAllocator
{
typedef Tp value_type;
SimpleAllocator()
{
}
template <class T> SimpleAllocator(const SimpleAllocator<T>& other)
{
}
Tp *allocate(std::size_t n)
{
return static_cast<Tp*>(::operator new(n * sizeof(Tp)));
}
void deallocate(Tp* p, std::size_t)
{
::operator delete((void*) p);
}
};
template <typename T>
void test_simple_allocator()
{
test::check_instances check_;
typedef boost::unordered::detail::allocator_traits<
SimpleAllocator<T> > traits;
BOOST_MPL_ASSERT((boost::is_same<typename traits::allocator_type, SimpleAllocator<T> >));
BOOST_MPL_ASSERT((boost::is_same<typename traits::value_type, T>));
BOOST_MPL_ASSERT((boost::is_same<typename traits::pointer, T* >));
BOOST_MPL_ASSERT((boost::is_same<typename traits::const_pointer, T const*>));
//BOOST_MPL_ASSERT((boost::is_same<typename traits::void_pointer, void* >));
//BOOST_MPL_ASSERT((boost::is_same<typename traits::const_void_pointer, void const*>));
BOOST_MPL_ASSERT((boost::is_same<typename traits::difference_type, std::ptrdiff_t>));
BOOST_MPL_ASSERT((boost::is_same<typename traits::size_type, std::size_t>));
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<T> a;
T* ptr1 = traits::allocate(a, 1);
//T* ptr2 = traits::allocate(a, 1, static_cast<void const*>(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<int>();
test_simple_allocator<test::object>();
return boost::report_errors();
}