multi_index/test/test_alloc_awareness.cpp
2020-01-26 11:52:23 +01:00

193 lines
5.4 KiB
C++

/* Boost.MultiIndex test for allocator awareness.
*
* Copyright 2003-2020 Joaquin M Lopez Munoz.
* 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)
*
* See http://www.boost.org/libs/multi_index for library home page.
*/
#include "test_alloc_awareness.hpp"
#include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
#include <boost/detail/lightweight_test.hpp>
#include <boost/move/core.hpp>
#include "pre_multi_index.hpp"
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/ranked_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <memory>
struct move_tracker
{
move_tracker(int n):n(n),move_cted(false){}
move_tracker(const move_tracker& x):n(x.n),move_cted(false){}
move_tracker(BOOST_RV_REF(move_tracker) x):n(x.n),move_cted(true){}
int n;
bool move_cted;
};
inline bool operator==(const move_tracker& x,const move_tracker& y)
{
return x.n==y.n;
}
inline bool operator<(const move_tracker& x,const move_tracker& y)
{
return x.n<y.n;
}
#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
namespace boost{
#endif
inline std::size_t hash_value(const move_tracker& x)
{
boost::hash<int> h;
return h(x.n);
}
#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
} /* namespace boost */
#endif
template<
typename T,
typename Propagate=boost::true_type,typename AlwaysEqual=boost::true_type
>
struct rooted_allocator:std::allocator<T>
{
typedef Propagate propagate_on_container_copy_assignment;
typedef Propagate propagate_on_container_move_assignment;
typedef Propagate propagate_on_container_swap;
typedef AlwaysEqual is_always_equal;
template<typename U>
struct rebind{typedef rooted_allocator<U,Propagate,AlwaysEqual> other;};
rooted_allocator():root(0){}
explicit rooted_allocator(int):root(this){}
template<typename U>
rooted_allocator(const rooted_allocator<U,Propagate,AlwaysEqual>& x):
root(x.root){}
template<typename U>
bool operator==(const rooted_allocator<U,Propagate,AlwaysEqual>& x)const
{return AlwaysEqual::value?true:root==x.root;}
template<typename U>
bool operator!=(const rooted_allocator<U,Propagate,AlwaysEqual>& x)const
{return !(*this==x);}
template<typename U>
bool comes_from(const rooted_allocator<U,Propagate,AlwaysEqual>& x)const
{return root==&x;}
private:
template<typename,typename,typename> friend struct rooted_allocator;
const void* root;
};
#if defined(BOOST_NO_CXX17_IF_CONSTEXPR)&&defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable:4127) /* conditional expression is constant */
#endif
template<bool Propagate,bool AlwaysEqual>
void test_allocator_awareness_for()
{
using namespace boost::multi_index;
typedef rooted_allocator<
move_tracker,
boost::integral_constant<bool, Propagate>,
boost::integral_constant<bool, AlwaysEqual>
> allocator;
typedef multi_index_container<
move_tracker,
indexed_by<
hashed_unique<identity<move_tracker> >,
ordered_unique<identity<move_tracker> >,
random_access<>,
ranked_unique<identity<move_tracker> >,
sequenced<>
>,
allocator
> container;
allocator root1(0),root2(0);
container c(root1);
for(int i=0;i<10;++i)c.emplace(i);
BOOST_TEST(c.get_allocator().comes_from(root1));
{
container c2(c,root2);
BOOST_TEST(c2.get_allocator().comes_from(root2));
BOOST_TEST(c2==c);
}
{
container c2(c);
const move_tracker* pfirst=&*c2.begin();
container c3(boost::move(c2),root2);
BOOST_TEST(c3.get_allocator().comes_from(root2));
BOOST_TEST(c3==c);
BOOST_TEST(c2.empty());
BOOST_TEST(AlwaysEqual==(&*c3.begin()==pfirst));
BOOST_TEST(!AlwaysEqual==(c3.begin()->move_cted));
}
{
container c2(root2);
c2=c;
BOOST_TEST(c2.get_allocator().comes_from(Propagate?root1:root2));
BOOST_TEST(c2==c);
}
{
container c2(c);
const move_tracker* pfirst=&*c2.begin();
container c3(root2);
c3=boost::move(c2);
BOOST_TEST(c3.get_allocator().comes_from(Propagate?root1:root2));
BOOST_TEST(c3==c);
BOOST_TEST(c2.empty());
BOOST_TEST(AlwaysEqual==(&*c3.begin()==pfirst));
BOOST_TEST(!AlwaysEqual==(c3.begin()->move_cted));
}
if(Propagate||AlwaysEqual){
container c2(c);
const move_tracker* pfirst=&*c2.begin();
container c3(root2);
c3.swap(c2);
BOOST_TEST(c2.get_allocator().comes_from(Propagate?root2:root1));
BOOST_TEST(c3.get_allocator().comes_from(Propagate?root1:root2));
BOOST_TEST(c3==c);
BOOST_TEST(c2.empty());
BOOST_TEST(&*c3.begin()==pfirst);
BOOST_TEST(!c3.begin()->move_cted);
}
}
#if defined(BOOST_NO_CXX17_IF_CONSTEXPR)&&defined(BOOST_MSVC)
#pragma warning(pop)
#endif
void test_allocator_awareness()
{
test_allocator_awareness_for<false,false>();
test_allocator_awareness_for<false,true>();
#if !defined(BOOST_NO_CXX11_ALLOCATOR)
/* only in C+11 onwards are allocators potentially expected to propagate */
test_allocator_awareness_for<true,false>();
test_allocator_awareness_for<true,true>();
#endif
}