Merge pull request #56 from LeonineKing1199/feature/allocator-traits

Replace internal implementation of `allocation_traits` with Core's
This commit is contained in:
Peter Dimov
2021-12-08 01:23:02 +02:00
committed by GitHub
2 changed files with 11 additions and 446 deletions

View File

@ -13,6 +13,7 @@
#endif
#include <boost/assert.hpp>
#include <boost/core/allocator_traits.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/pointer_traits.hpp>
#include <boost/detail/select_type.hpp>
@ -1055,468 +1056,24 @@ namespace boost {
//
// Allocator traits
//
// First our implementation, then later light wrappers around the alternatives
#if BOOST_UNORDERED_USE_ALLOCATOR_TRAITS == 0
#include <boost/limits.hpp>
#include <boost/pointer_to_other.hpp>
#include <boost/utility/enable_if.hpp>
namespace boost {
namespace unordered {
namespace detail {
template <typename Alloc, typename T> struct rebind_alloc;
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <template <typename, typename...> class Alloc, typename U,
typename T, typename... Args>
struct rebind_alloc<Alloc<U, Args...>, T>
{
typedef Alloc<T, Args...> type;
};
#else
template <template <typename> class Alloc, typename U, typename T>
struct rebind_alloc<Alloc<U>, T>
{
typedef Alloc<T> type;
};
template <template <typename, typename> class Alloc, typename U,
typename T, typename A0>
struct rebind_alloc<Alloc<U, A0>, T>
{
typedef Alloc<T, A0> type;
};
template <template <typename, typename, typename> class Alloc, typename U,
typename T, typename A0, typename A1>
struct rebind_alloc<Alloc<U, A0, A1>, T>
{
typedef Alloc<T, A0, A1> type;
};
#endif
template <typename Alloc, typename T> struct rebind_wrap
{
template <typename X>
static choice1::type test(
choice1, typename X::BOOST_NESTED_TEMPLATE rebind<T>::other* = 0);
template <typename X> static choice2::type test(choice2, void* = 0);
enum
{
value = (1 == sizeof(test<Alloc>(choose())))
};
struct fallback
{
template <typename U> struct rebind
{
typedef typename rebind_alloc<Alloc, T>::type other;
};
};
typedef
typename boost::detail::if_true<value>::BOOST_NESTED_TEMPLATE then<
Alloc, fallback>::type::BOOST_NESTED_TEMPLATE rebind<T>::other type;
};
}
}
}
namespace boost {
namespace unordered {
namespace detail {
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);
BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(is_always_equal);
#if !defined(BOOST_NO_SFINAE_EXPR)
template <typename T>
BOOST_UNORDERED_HAS_FUNCTION(
select_on_container_copy_construction, U const, (), 0);
template <typename T>
BOOST_UNORDERED_HAS_FUNCTION(max_size, U const, (), 0);
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <typename T, typename ValueType, typename... Args>
BOOST_UNORDERED_HAS_FUNCTION(construct, U,
(boost::unordered::detail::make<ValueType*>(),
std::declval<Args>()...),
2);
#else
template <typename T, typename ValueType>
BOOST_UNORDERED_HAS_FUNCTION(construct, U,
(boost::unordered::detail::make<ValueType*>(),
boost::unordered::detail::make<ValueType const>()),
2);
#endif
template <typename T, typename ValueType>
BOOST_UNORDERED_HAS_FUNCTION(
destroy, U, (boost::unordered::detail::make<ValueType*>()), 1);
#else
template <typename T>
BOOST_UNORDERED_HAS_MEMBER(select_on_container_copy_construction);
template <typename T> BOOST_UNORDERED_HAS_MEMBER(max_size);
template <typename T, typename ValueType>
BOOST_UNORDERED_HAS_MEMBER(construct);
template <typename T, typename ValueType>
BOOST_UNORDERED_HAS_MEMBER(destroy);
#endif
}
}
}
namespace boost {
namespace unordered {
namespace detail {
namespace func {
template <typename Alloc>
inline typename boost::enable_if<
boost::unordered::detail::has_select_on_container_copy_construction<
Alloc>,
Alloc>::type
call_select_on_container_copy_construction(const Alloc& rhs)
{
return rhs.select_on_container_copy_construction();
}
template <typename Alloc>
inline typename boost::disable_if<
boost::unordered::detail::has_select_on_container_copy_construction<
Alloc>,
Alloc>::type
call_select_on_container_copy_construction(const Alloc& rhs)
{
return rhs;
}
template <typename SizeType, typename Alloc>
inline typename boost::enable_if<
boost::unordered::detail::has_max_size<Alloc>, SizeType>::type
call_max_size(const Alloc& a)
{
return a.max_size();
}
template <typename SizeType, typename Alloc>
inline typename boost::disable_if<
boost::unordered::detail::has_max_size<Alloc>, SizeType>::type
call_max_size(const Alloc&)
{
return (std::numeric_limits<SizeType>::max)();
}
} // namespace func.
}
}
}
namespace boost {
namespace unordered {
namespace detail {
template <typename Alloc> struct allocator_traits
{
typedef Alloc allocator_type;
typedef typename Alloc::value_type value_type;
typedef BOOST_UNORDERED_DEFAULT_TYPE(
Alloc, pointer, value_type*) pointer;
template <typename T>
struct pointer_to_other : boost::pointer_to_other<pointer, T>
{
};
typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, const_pointer,
typename pointer_to_other<const value_type>::type) const_pointer;
// typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, void_pointer,
// typename pointer_to_other<void>::type)
// void_pointer;
//
// typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, const_void_pointer,
// typename pointer_to_other<const void>::type)
// const_void_pointer;
typedef BOOST_UNORDERED_DEFAULT_TYPE(
Alloc, difference_type, std::ptrdiff_t) difference_type;
typedef BOOST_UNORDERED_DEFAULT_TYPE(
Alloc, size_type, std::size_t) size_type;
#if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES)
template <typename T>
using rebind_alloc = typename rebind_wrap<Alloc, T>::type;
template <typename T>
using rebind_traits =
boost::unordered::detail::allocator_traits<rebind_alloc<T> >;
#endif
static pointer allocate(Alloc& a, size_type n) { return a.allocate(n); }
// 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)
// { return DEFAULT_FUNC(allocate, pointer)(a, n, hint); }
static void deallocate(Alloc& a, pointer p, size_type n)
{
a.deallocate(p, n);
}
public:
#if BOOST_UNORDERED_CXX11_CONSTRUCTION
template <typename T, typename... Args>
static
typename boost::enable_if_c<boost::unordered::detail::has_construct<
Alloc, T, Args...>::value>::type
construct(Alloc& a, T* p, BOOST_FWD_REF(Args)... x)
{
a.construct(p, boost::forward<Args>(x)...);
}
template <typename T, typename... Args>
static
typename boost::disable_if_c<boost::unordered::detail::has_construct<
Alloc, T, Args...>::value>::type
construct(Alloc&, T* p, BOOST_FWD_REF(Args)... x)
{
new (static_cast<void*>(p)) T(boost::forward<Args>(x)...);
}
template <typename T>
static typename boost::enable_if_c<
boost::unordered::detail::has_destroy<Alloc, T>::value>::type
destroy(Alloc& a, T* p)
{
a.destroy(p);
}
template <typename T>
static typename boost::disable_if_c<
boost::unordered::detail::has_destroy<Alloc, T>::value>::type
destroy(Alloc&, T* p)
{
boost::unordered::detail::func::destroy(p);
}
#elif !defined(BOOST_NO_SFINAE_EXPR)
template <typename T>
static typename boost::enable_if_c<
boost::unordered::detail::has_construct<Alloc, T>::value>::type
construct(Alloc& a, T* p, T const& x)
{
a.construct(p, x);
}
template <typename T>
static typename boost::disable_if_c<
boost::unordered::detail::has_construct<Alloc, T>::value>::type
construct(Alloc&, T* p, T const& x)
{
new (static_cast<void*>(p)) T(x);
}
template <typename T>
static typename boost::enable_if_c<
boost::unordered::detail::has_destroy<Alloc, T>::value>::type
destroy(Alloc& a, T* p)
{
a.destroy(p);
}
template <typename T>
static typename boost::disable_if_c<
boost::unordered::detail::has_destroy<Alloc, T>::value>::type
destroy(Alloc&, T* p)
{
boost::unordered::detail::func::destroy(p);
}
#else
// If we don't have SFINAE expressions, only call construct for the
// copy constructor for the allocator's value_type - as that's
// the only construct method that old fashioned allocators support.
template <typename T>
static typename boost::enable_if_c<
boost::unordered::detail::has_construct<Alloc, T>::value &&
boost::is_same<T, value_type>::value,
void>::type
construct(Alloc& a, T* p, T const& x)
{
a.construct(p, x);
}
template <typename T>
static typename boost::disable_if_c<
boost::unordered::detail::has_construct<Alloc, T>::value &&
boost::is_same<T, value_type>::value,
void>::type
construct(Alloc&, T* p, T const& x)
{
new (static_cast<void*>(p)) T(x);
}
template <typename T>
static typename boost::enable_if_c<
boost::unordered::detail::has_destroy<Alloc, T>::value &&
boost::is_same<T, value_type>::value,
void>::type
destroy(Alloc& a, T* p)
{
a.destroy(p);
}
template <typename T>
static typename boost::disable_if_c<
boost::unordered::detail::has_destroy<Alloc, T>::value &&
boost::is_same<T, value_type>::value,
void>::type
destroy(Alloc&, T* p)
{
boost::unordered::detail::func::destroy(p);
}
#endif
static size_type max_size(const Alloc& a)
{
return boost::unordered::detail::func::call_max_size<size_type>(a);
}
// Allocator propagation on construction
static Alloc select_on_container_copy_construction(Alloc const& rhs)
{
return boost::unordered::detail::func::
call_select_on_container_copy_construction(rhs);
}
// Allocator propagation on assignment and swap.
// Return true if lhs is modified.
typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc,
propagate_on_container_copy_assignment,
false_type) propagate_on_container_copy_assignment;
typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc,
propagate_on_container_move_assignment,
false_type) propagate_on_container_move_assignment;
typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, propagate_on_container_swap,
false_type) propagate_on_container_swap;
typedef BOOST_UNORDERED_DEFAULT_TYPE(Alloc, is_always_equal,
typename boost::is_empty<Alloc>::type) is_always_equal;
};
}
}
}
#undef BOOST_UNORDERED_DEFAULT_TYPE_TMPLT
#undef BOOST_UNORDERED_DEFAULT_TYPE
////////////////////////////////////////////////////////////////////////////////
//
// std::allocator_traits
#elif BOOST_UNORDERED_USE_ALLOCATOR_TRAITS == 1
#include <memory>
namespace boost {
namespace unordered {
namespace detail {
BOOST_UNORDERED_DEFAULT_TYPE_TMPLT(is_always_equal);
template <typename Alloc>
struct allocator_traits : std::allocator_traits<Alloc>
{
// As is_always_equal was introduced in C++17, std::allocator_traits
// doesn't always have it. So use it when available, implement it
// ourselves when not. Would be simpler not to bother with
// std::allocator_traits, but I feel like I should try to use
// it where possible.
typedef BOOST_UNORDERED_DEFAULT_TYPE(std::allocator_traits<Alloc>,
is_always_equal,
BOOST_UNORDERED_DEFAULT_TYPE(Alloc, is_always_equal,
typename boost::is_empty<Alloc>::type)) is_always_equal;
};
template <typename Alloc, typename T> struct rebind_wrap
{
typedef typename std::allocator_traits<Alloc>::template rebind_alloc<T>
type;
};
}
}
}
////////////////////////////////////////////////////////////////////////////////
//
// boost::container::allocator_traits
#elif BOOST_UNORDERED_USE_ALLOCATOR_TRAITS == 2
#include <boost/container/allocator_traits.hpp>
namespace boost {
namespace unordered {
namespace detail {
template <typename Alloc>
struct allocator_traits : boost::container::allocator_traits<Alloc>
struct allocator_traits : boost::allocator_traits<Alloc>
{
};
template <typename Alloc, typename T>
struct rebind_wrap : boost::container::allocator_traits<
Alloc>::template portable_rebind_alloc<T>
struct rebind_wrap : boost::allocator_rebind<Alloc, T>
{
};
}
}
}
#else
#error "Invalid BOOST_UNORDERED_USE_ALLOCATOR_TRAITS value."
#endif
////////////////////////////////////////////////////////////////////////////
// Functions used to construct nodes. Emulates variadic construction,
// piecewise construction etc.

View File

@ -213,7 +213,15 @@ void test_allocator2()
BOOST_TEST(!traits::propagate_on_container_move_assignment::value);
BOOST_TEST(!traits::propagate_on_container_swap::value);
BOOST_TEST(!traits::is_always_equal::value);
#if !defined(BOOST_NO_CXX11_ALLOCATOR)
// conditionally compile this assertion as all C++03 emulations of expression
// SFINAE are broken one way or another and the benefits of using Core's
// `allocator_traits` outweigh the costs of breaking this kind of code (i.e.
// inheriting SOCCC via a base)
//
BOOST_TEST(call_select<allocator>() == 1);
#endif
}
// allocator 3