diff --git a/include/boost/unordered/detail/allocator_helpers.hpp b/include/boost/unordered/detail/allocator_helpers.hpp index 79f40467..7f79dd2e 100644 --- a/include/boost/unordered/detail/allocator_helpers.hpp +++ b/include/boost/unordered/detail/allocator_helpers.hpp @@ -5,8 +5,8 @@ // 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) // -// Written by Daniel James using some code from Pablo Halpern's -// allocator traits implementation. +// Allocator traits written by Daniel James based on Pablo Halpern's +// implementation. #ifndef BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED #define BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED @@ -18,6 +18,7 @@ #include #include #include +#include #if (defined(BOOST_NO_STD_ALLOCATOR) || defined(BOOST_DINKUMWARE_STDLIB)) \ && !defined(__BORLANDC__) @@ -81,38 +82,39 @@ namespace boost { namespace unordered { namespace detail { }; # endif - struct convertible_from_anything - { - template convertible_from_anything(T const&); - }; + template T& make(); + struct choice9 { typedef char (&type)[9]; }; + struct choice8 : choice9 { typedef char (&type)[8]; }; + struct choice7 : choice8 { typedef char (&type)[7]; }; + struct choice6 : choice7 { typedef char (&type)[6]; }; + struct choice5 : choice6 { typedef char (&type)[5]; }; + struct choice4 : choice5 { typedef char (&type)[4]; }; + struct choice3 : choice4 { typedef char (&type)[3]; }; + struct choice2 : choice3 { typedef char (&type)[2]; }; + struct choice1 : choice2 { typedef char (&type)[1]; }; + choice1 choose(); - typedef char (&no_type)[1]; - typedef char (&yes_type)[2]; - - template struct sfinae { - typedef yes_type type; - }; - - // Infrastructure for providing a default type for Tp::tname if absent. #define BOOST_DEFAULT_TYPE_TMPLT(tname) \ template \ struct default_type_ ## tname { \ - template \ - static BOOST_DEDUCED_TYPENAME sfinae< \ - BOOST_DEDUCED_TYPENAME T::tname>::type test(int); \ - template \ - static no_type test(long); \ \ - enum { value = sizeof(test(0)) == sizeof(yes_type) }; \ + template \ + static choice1::type test(choice1, \ + BOOST_DEDUCED_TYPENAME X::tname* = 0); \ + \ + template \ + static choice2::type test(choice2, void* = 0); \ \ struct DefaultWrap { typedef Default tname; }; \ \ + enum { value = (1 == sizeof(test(choose()))) }; \ + \ typedef BOOST_DEDUCED_TYPENAME \ boost::detail::if_true:: \ BOOST_NESTED_TEMPLATE then \ ::type::tname type; \ } - + #define BOOST_DEFAULT_TYPE(T,tname, arg) \ BOOST_DEDUCED_TYPENAME default_type_ ## tname::type @@ -127,47 +129,62 @@ namespace boost { namespace unordered { namespace detail { BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_swap); #if !defined(BOOST_NO_SFINAE_EXPR) || BOOST_WORKAROUND(BOOST_MSVC, >= 1500) - // Specialization is only needed for Visual C++. Without it SFINAE doesn't - // kick in. - template - struct expr_sfinae; - - template <> - struct expr_sfinae { - typedef yes_type type; - }; - + + template struct expr_test; + template struct expr_test : T {}; + template static char for_expr_test(U const&); + +#define BOOST_UNORDERED_CHECK_EXPRESSION(count, result, expression) \ + template \ + static typename expr_test< \ + BOOST_PP_CAT(choice, result), \ + sizeof(for_expr_test(((expression), 0)))>::type test( \ + BOOST_PP_CAT(choice, count)) + +#define BOOST_UNORDERED_DEFAULT_EXPRESSION(count, result) \ + template \ + static BOOST_PP_CAT(choice, result)::type test( \ + BOOST_PP_CAT(choice, count)); + template struct has_select_on_container_copy_construction { - // This needs to be a template for Visual C++. - template - static yes_type to_yes_type(const T2&); - - template - static typename expr_sfinaeselect_on_container_copy_construction() - ))>::type check(T2*); - - static no_type check(void*); + BOOST_UNORDERED_CHECK_EXPRESSION(1, 1, make().select_on_container_copy_construction()); + BOOST_UNORDERED_DEFAULT_EXPRESSION(2, 2); - enum { value = sizeof(check((T*) 0)) == sizeof(yes_type) }; + enum { value = sizeof(test(choose())) == sizeof(choice1::type) }; }; + #else + + template struct identity { typedef T type; }; + +#define BOOST_UNORDERED_CHECK_MEMBER(count, result, name, member) \ + \ + typedef typename identity::type BOOST_PP_CAT(check, count); \ + \ + template \ + struct BOOST_PP_CAT(test, count) { \ + typedef void* type; \ + }; \ + \ + template static BOOST_PP_CAT(choice, result)::type \ + test(BOOST_PP_CAT(choice, count), \ + typename BOOST_PP_CAT(test, count)< \ + &U::name>::type = 0) + +#define BOOST_UNORDERED_DEFAULT_MEMBER(count, result) \ + template static BOOST_PP_CAT(choice, result)::type \ + test(BOOST_PP_CAT(choice, count), void* = 0) + + template struct has_select_on_container_copy_construction { - typedef T (T::*SelectFunc)() const; - - template struct sfinae { typedef yes_type type; }; - - template - static typename sfinae<&U::select_on_container_copy_construction>::type - test(int); - template - static no_type test(...); + BOOST_UNORDERED_CHECK_MEMBER(1, 1, select_on_container_copy_construction, T (T::*)() const); + BOOST_UNORDERED_DEFAULT_MEMBER(2, 2); - enum { value = sizeof(test(1)) == sizeof(yes_type) }; + enum { value = sizeof(test(choose())) == sizeof(choice1::type) }; }; #endif diff --git a/test/helpers/memory.hpp b/test/helpers/memory.hpp index 102fd16e..af75328a 100644 --- a/test/helpers/memory.hpp +++ b/test/helpers/memory.hpp @@ -194,8 +194,13 @@ namespace test enum { value = false }; }; - template - int selected_count(Alloc const&) + struct convert_from_anything + { + template + convert_from_anything(T const&) {} + }; + + int selected_count(convert_from_anything) { return 0; } diff --git a/test/objects/cxx11_allocator.hpp b/test/objects/cxx11_allocator.hpp index f909b663..bb51713a 100644 --- a/test/objects/cxx11_allocator.hpp +++ b/test/objects/cxx11_allocator.hpp @@ -186,9 +186,14 @@ namespace test }; template - struct cxx11_allocator : - public cxx11_allocator_base, + typename Enable = void> + struct cxx11_allocator; + + template + struct cxx11_allocator< + T, Flags, + typename boost::disable_if_c::type + > : public cxx11_allocator_base, public swap_allocator_base, public assign_allocator_base, public move_allocator_base, @@ -228,8 +233,10 @@ namespace test }; template - struct cxx11_allocator : - public cxx11_allocator_base, + struct cxx11_allocator< + T, Flags, + typename boost::enable_if_c::type + > : public cxx11_allocator_base, public swap_allocator_base, public assign_allocator_base, public move_allocator_base, diff --git a/test/objects/test.hpp b/test/objects/test.hpp index d1fe5c3b..fc690c13 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -26,7 +26,7 @@ namespace test object generate(object const*); implicitly_convertible generate(implicitly_convertible const*); - class object : globally_counted_object + class object : private globally_counted_object { friend class hash; friend class equal_to; @@ -64,7 +64,7 @@ namespace test } }; - class implicitly_convertible : globally_counted_object + class implicitly_convertible : private globally_counted_object { int tag1_, tag2_; public: diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index 8c3fbbcf..9919f61f 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -22,6 +22,7 @@ test-suite unordered : [ run fwd_set_test.cpp ] [ run fwd_map_test.cpp ] + [ run allocator_traits.cpp ] [ run compile_set.cpp ] [ run compile_map.cpp ] [ run link_test_1.cpp link_test_2.cpp ] diff --git a/test/unordered/allocator_traits.cpp b/test/unordered/allocator_traits.cpp new file mode 100644 index 00000000..48d6a9e4 --- /dev/null +++ b/test/unordered/allocator_traits.cpp @@ -0,0 +1,226 @@ + +// 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 + +// Boilerplate + +#define ALLOCATOR_METHODS(name) \ + template struct rebind { \ + typedef name other; \ + }; \ + \ + name() {} \ + template name(name const&) {} \ + T* address(T& r) { return &r;} \ + T const* address(T const& r) { return &r; } \ + T* allocate(std::size_t n) \ + { return static_cast(::operator new(n * sizeof(T))); } \ + T* allocate(std::size_t n, void const* u) \ + { return static_cast(::operator new(n * sizeof(T))); } \ + void deallocate(T* p, std::size_t n) { ::operator delete((void*) p); } \ + void construct(T* p, T const& t) { new(p) T(t); } \ + void destroy(T* p) { p->~T(); } \ + std::size_t max_size() const \ + { return (std::numeric_limits::max)(); } \ + bool operator==(name const&) { return true; } \ + bool operator!=(name const&) { return false; } \ +/**/ + +#define ALLOCATOR_METHODS_TYPEDEFS(name) \ + template struct rebind { \ + typedef name other; \ + }; \ + \ + name() {} \ + template name(name const&) {} \ + pointer address(T& r) { return &r;} \ + const_pointer address(T const& r) { return &r; } \ + pointer allocate(std::size_t n) \ + { return pointer(::operator new(n * sizeof(T))); } \ + pointer allocate(std::size_t n, void const* u) \ + { return pointer(::operator new(n * sizeof(T))); } \ + void deallocate(pointer p, std::size_t n) \ + { ::operator delete((void*) p); } \ + void construct(T* p, T const& t) { new(p) T(t); } \ + void destroy(T* p) { p->~T(); } \ + size_type max_size() const \ + { return (std::numeric_limits::max)(); } \ + bool operator==(name const&) { return true; } \ + bool operator!=(name const&) { return false; } \ +/**/ + +struct yes_type { enum { value = true }; }; +struct no_type { enum { value = false }; }; + +// For tracking calls... + +static int selected; +void reset() { + selected = 0; +} + +template +int call_select() +{ + typedef boost::unordered::detail::allocator_traits traits; + Allocator a; + + reset(); + BOOST_TEST(traits::select_on_container_copy_construction(a) == a); + return selected; +} + +// Empty allocator test + +template +struct empty_allocator +{ + typedef T value_type; + ALLOCATOR_METHODS(empty_allocator) +}; + +void test_empty_allocator() +{ + typedef empty_allocator allocator; + typedef boost::unordered::detail::allocator_traits 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_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); + BOOST_TEST(call_select() == 0); +} + +// allocator 1 + +template +struct allocator1 +{ + typedef T value_type; + ALLOCATOR_METHODS(allocator1) + + typedef yes_type propagate_on_container_copy_assignment; + typedef yes_type propagate_on_container_move_assignment; + typedef yes_type propagate_on_container_swap; + + allocator1 select_on_container_copy_construction() const { + ++selected; + return allocator1(); + } +}; + +void test_allocator1() +{ + typedef allocator1 allocator; + typedef boost::unordered::detail::allocator_traits 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_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); + BOOST_TEST(call_select() == 1); +} + +// allocator 2 + +template +struct allocator2 +{ + typedef T value_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef std::size_t size_type; + + ALLOCATOR_METHODS(allocator2) + + typedef no_type propagate_on_container_copy_assignment; + typedef no_type propagate_on_container_move_assignment; + typedef no_type propagate_on_container_swap; + + // Note: Not const - so it shouldn't be called. + allocator2 select_on_container_copy_construction() { + ++selected; + return allocator2(); + } +}; + +void test_allocator2() +{ + typedef allocator2 allocator; + typedef boost::unordered::detail::allocator_traits 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_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); + BOOST_TEST(call_select() == 0); +} + +// allocator 3 + +template +struct ptr +{ + T* value_; + + ptr(void* v) : value_((T*) v) {} + T& operator*() const { return *value_; } +}; + +template +struct allocator3 +{ + typedef T value_type; + typedef ptr pointer; + typedef ptr const_pointer; + typedef unsigned short size_type; + + ALLOCATOR_METHODS_TYPEDEFS(allocator3) + + typedef yes_type propagate_on_container_copy_assignment; + typedef no_type propagate_on_container_move_assignment; + + allocator3 select_on_container_copy_construction() const { + ++selected; + return allocator3(); + } +}; + +void test_allocator3() +{ + typedef allocator3 allocator; + typedef boost::unordered::detail::allocator_traits 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_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); + BOOST_TEST(call_select() == 1); +} + +int main() +{ + test_empty_allocator(); + test_allocator1(); + test_allocator2(); + test_allocator3(); + return boost::report_errors(); +}