/* Boost.MultiIndex test for node handling operations. * * Copyright 2003-2021 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_node_handling.hpp" #include /* keep it first to prevent nasty warns in MSVC */ #include #include #include #include "pre_multi_index.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "count_allocator.hpp" using namespace boost::multi_index; void test_node_handle() { typedef count_allocator allocator; typedef multi_index_container< int, indexed_by< ordered_unique > >, allocator > container; typedef container::node_type node_type; std::size_t element_count=0,allocator_count=0; container c((allocator(element_count,allocator_count))); element_count=0; /* ignore non element-related allocations */ allocator_count=0; /* ignore internal allocator(s) */ c.insert(0); c.insert(1); c.insert(2); const int* addr0=&*c.find(0); const int* addr1=&*c.find(1); BOOST_TEST(element_count==3); node_type n1; BOOST_TEST(n1.empty()); BOOST_TEST(!n1); BOOST_TEST(allocator_count==0); node_type n2=c.extract(0); BOOST_TEST(!n2.empty()); BOOST_TEST((bool)n2); BOOST_TEST(n2.value()==0); BOOST_TEST(&n2.value()==addr0); BOOST_TEST(allocator_count==1); node_type n3(boost::move(n2)); BOOST_TEST(n2.empty()); BOOST_TEST(!n3.empty()); BOOST_TEST(&n3.value()==addr0); BOOST_TEST(allocator_count==1); node_type n4(boost::move(n2)); BOOST_TEST(n4.empty()); BOOST_TEST(allocator_count==1); n1=boost::move(n3); BOOST_TEST(!n1.empty()); BOOST_TEST(&n1.value()==addr0); BOOST_TEST(n3.empty()); BOOST_TEST(allocator_count==1); BOOST_TEST(n1.get_allocator()==c.get_allocator()); node_type n5=c.extract(1); BOOST_TEST(n5.value()==1); BOOST_TEST(&n5.value()==addr1); BOOST_TEST(allocator_count==2); n1.swap(n5); BOOST_TEST(&n1.value()==addr1); BOOST_TEST(&n5.value()==addr0); BOOST_TEST(allocator_count==2); using std::swap; swap(n2,n3); BOOST_TEST(n2.empty()); BOOST_TEST(n3.empty()); BOOST_TEST(allocator_count==2); swap(n1,n2); BOOST_TEST(!n2.empty()); BOOST_TEST(&n2.value()==addr1); BOOST_TEST(allocator_count==2); swap(n1,n2); BOOST_TEST(&n1.value()==addr1); BOOST_TEST(n2.empty()); BOOST_TEST(allocator_count==2); n2=boost::move(n3); BOOST_TEST(n2.empty()); BOOST_TEST(n3.empty()); BOOST_TEST(allocator_count==2); BOOST_TEST(element_count==3); n1=boost::move(n5); BOOST_TEST(&n1.value()==addr0); BOOST_TEST(n5.empty()); BOOST_TEST(element_count==2); BOOST_TEST(allocator_count==1); n1=boost::move(n5); BOOST_TEST(n1.empty()); BOOST_TEST(element_count==1); BOOST_TEST(allocator_count==0); c.extract(2); BOOST_TEST(element_count==0); } template struct is_key_based:boost::integral_constant< bool, /* rather fragile if new index types are included in the library */ (boost::tuples::length:: type::ctor_args>::value > 0) > {}; template struct is_iterator { typedef char yes; struct no{char m[2];}; template static no test(...); template static yes test(typename Q::iterator_category*); BOOST_STATIC_CONSTANT(bool,value=(sizeof(test(0))==sizeof(yes))); }; template struct enable_if_not_iterator:boost::enable_if_c< !is_iterator::value, void* >{}; template void test_transfer_result( Dst&,Ret res,const NodeHandle& n,const Value& x, typename enable_if_not_iterator::type=0) { BOOST_TEST(*(res.position)==x); if(res.inserted){ BOOST_TEST(res.node.empty()); } else{ BOOST_TEST(res.node.value()==x); } BOOST_TEST(n.empty()); } template void test_transfer_result( Dst&,typename Dst::iterator res,const NodeHandle& n,const Value& x) { BOOST_TEST(*res==x); if(n)BOOST_TEST(n.value()==x); } template void test_transfer_result_empty( Dst& dst,Ret res, typename enable_if_not_iterator::type=0) { BOOST_TEST(res.position==dst.end()); BOOST_TEST(!res.inserted); BOOST_TEST(res.node.empty()); } template void test_transfer_result_empty(Dst& dst,typename Dst::iterator res) { BOOST_TEST(res==dst.end()); } template void test_transfer_result( Dst& dst,typename Dst::iterator pos,Ret res, const NodeHandle& n,const Value& x, typename enable_if_not_iterator::type=0) { if(res.inserted&&pos!=dst.end()&& (!is_key_based::value||*pos==x)){ BOOST_TEST(boost::next(res.position)==pos); } test_transfer_result(dst,Ret(boost::move(res)),n,x); } template void test_transfer_result( Dst& dst,typename Dst::iterator pos, typename Dst::iterator res,const NodeHandle& n,const Value& x) { if(n.empty()&&pos!=dst.end()&& (!is_key_based::value||*pos==x)){ BOOST_TEST(boost::next(res)==pos); } test_transfer_result(dst,res,n,x); } template void test_transfer_result_empty( Dst& dst,typename Dst::iterator,Ret res, typename enable_if_not_iterator::type=0) { test_transfer_result_empty(dst,Ret(boost::move(res))); } template void test_transfer_result_empty( Dst& dst,typename Dst::iterator,typename Dst::iterator res) { test_transfer_result_empty(dst,res); } template typename Src::node_type checked_extract( Src& src,Key k, typename enable_if_not_iterator::type=0) { typename Src::node_type n=src.extract(k); if(n)BOOST_TEST(src.key_extractor()(n.value())==k); return boost::move(n); } template typename Src::node_type checked_extract(Src& src,typename Src::iterator pos) { typename Src::value_type x=*pos; typename Src::node_type n=src.extract(pos); if(n)BOOST_TEST(n.value()==x); return boost::move(n); } template void test_transfer(Src& src,Locator loc,Dst& dst) { typename Dst::node_type n=checked_extract(src,loc); if(n){ typename Dst::value_type x=n.value(); test_transfer_result(dst,dst.insert(boost::move(n)),n,x); } else{ test_transfer_result_empty(dst,dst.insert(boost::move(n))); } } template void test_transfer(Src& src,Locator loc,Dst& dst,Iterator pos) { typename Dst::node_type n=checked_extract(src,loc); if(n){ typename Dst::value_type x=n.value(); test_transfer_result(dst,pos,dst.insert(pos,boost::move(n)),n,x); } else{ test_transfer_result_empty(dst,pos,dst.insert(pos,boost::move(n))); } } template void test_transfer( Src& src,Dst& dst0,Dst& /* dst1 */,Dst& /* dst2 */,Dst& /* dst3 */, boost::false_type /* Src key-based */,boost::false_type /* Dst key-based */) { test_transfer(src,src.begin(),dst0,dst0.begin()); test_transfer(src,src.begin(),dst0,dst0.begin()); for(int i=0;i<6;++i)src.extract(src.begin()); } template void test_transfer( Src& src,Dst& dst0,Dst& dst1,Dst& /* dst2 */,Dst& /* dst3 */, boost::false_type /* Src key-based */,boost::true_type /* Dst key-based */) { test_transfer(src,src.begin(),dst0); test_transfer(src,src.begin(),dst0); test_transfer(src,src.begin(),dst1,dst1.find(*src.begin())); test_transfer(src,src.begin(),dst1,dst1.find(*src.begin())); for(int i=0;i<4;++i)src.extract(src.begin()); } template void test_transfer( Src& src,Dst& dst0,Dst& dst1,Dst& /* dst2 */,Dst& /* dst3 */, boost::true_type /* Src key-based */,boost::false_type /* Dst key-based */) { test_transfer(src, src.begin(),dst0,dst0.begin()); test_transfer(src, src.begin(),dst0,dst0.begin()); test_transfer(src,*src.begin(),dst1,dst1.begin()); test_transfer(src,*src.begin(),dst1,dst1.begin()); test_transfer(src, -1,dst1,dst1.begin()); for(int i=0;i<4;++i)src.extract(src.begin()); } template void test_transfer( Src& src,Dst& dst0,Dst& dst1,Dst& dst2,Dst& dst3, boost::true_type /* Src key-based */,boost::true_type /* Dst key-based */) { test_transfer(src, src.begin(),dst0); test_transfer(src, src.begin(),dst0); test_transfer(src,*src.begin(),dst1); test_transfer(src,*src.begin(),dst1); test_transfer(src, -1,dst1); test_transfer(src, src.begin(),dst2,dst2.find(*src.begin())); test_transfer(src, src.begin(),dst2,dst2.find(*src.begin())); test_transfer(src,*src.begin(),dst3,dst3.find(*src.begin())); test_transfer(src,*src.begin(),dst3,dst3.find(*src.begin())); test_transfer(src, -1,dst3,dst3.begin()); } template void test_transfer(Src& src,Dst& dst0,Dst& dst1,Dst& dst2,Dst& dst3) { test_transfer( src,dst0,dst1,dst2,dst3,is_key_based(),is_key_based()); } void test_transfer() { typedef multi_index_container< int, indexed_by< hashed_non_unique >, ordered_non_unique >, random_access<>, sequenced<>, ranked_non_unique > > > container1; typedef multi_index_container< int, indexed_by< hashed_non_unique >, ordered_unique,std::greater >, random_access<>, sequenced<>, ranked_unique,std::greater > > > container2; container1 src; container1::nth_index<0>::type& src0=src.get<0>(); container1::nth_index<1>::type& src1=src.get<1>(); container1::nth_index<2>::type& src2=src.get<2>(); container1::nth_index<3>::type& src3=src.get<3>(); container1::nth_index<4>::type& src4=src.get<4>(); container2 dst0,dst1,dst2,dst3; container2::nth_index<0>::type& dst00=dst0.get<0>(), & dst10=dst1.get<0>(), & dst20=dst2.get<0>(), & dst30=dst3.get<0>(); container2::nth_index<1>::type& dst01=dst0.get<1>(), & dst11=dst1.get<1>(), & dst21=dst2.get<1>(), & dst31=dst3.get<1>(); container2::nth_index<2>::type& dst02=dst0.get<2>(), & dst12=dst1.get<2>(), & dst22=dst2.get<2>(), & dst32=dst3.get<2>(); container2::nth_index<3>::type& dst03=dst0.get<3>(), & dst13=dst1.get<3>(), & dst23=dst2.get<3>(), & dst33=dst3.get<3>(); container2::nth_index<4>::type& dst04=dst0.get<4>(), & dst14=dst1.get<4>(), & dst24=dst2.get<4>(), & dst34=dst3.get<4>(); for(int i=0;i<6;++i){ for(int j=0;j<8;++j)src.insert(i); } test_transfer(src0,dst01,dst11,dst21,dst31); test_transfer(src1,dst02,dst12,dst22,dst32); test_transfer(src2,dst03,dst13,dst23,dst33); test_transfer(src3,dst04,dst14,dst24,dst34); test_transfer(src4,dst00,dst10,dst20,dst30); BOOST_TEST(src.size()==8); } template struct enable_if_key_based:boost::enable_if_c< is_key_based::value, void* >{}; template struct enable_if_not_key_based:boost::enable_if_c< !is_key_based::value, void* >{}; /* Boost.Move C++03 perfect forwarding emulation converts non-const lvalue * refs to const lvalue refs. final_forward undoes that. */ #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template T&& final_forward(typename boost::remove_reference::type& x) { return static_cast(x); } #else template T& final_forward(const T& x){return const_cast(x);} #endif template void merge( Dst& dst,BOOST_FWD_REF(Src) src, typename enable_if_key_based::type=0) { dst.merge(final_forward(src)); } template void merge( Dst& dst,BOOST_FWD_REF(Src) src, typename enable_if_not_key_based::type=0) { dst.splice(dst.end(),final_forward(src)); } template std::pair merge( Dst& dst,BOOST_FWD_REF(Src) src, typename boost::remove_reference::type::iterator i, typename enable_if_key_based::type=0) { return dst.merge(final_forward(src),i); } template std::pair merge( Dst& dst,BOOST_FWD_REF(Src) src, typename boost::remove_reference::type::iterator i, typename enable_if_not_key_based::type=0) { return dst.splice(dst.end(),final_forward(src),i); } template void merge( Dst& dst,BOOST_FWD_REF(Src) src, typename boost::remove_reference::type::iterator first, typename boost::remove_reference::type::iterator last, typename enable_if_key_based::type=0) { dst.merge(final_forward(src),first,last); } template void merge( Dst& dst,BOOST_FWD_REF(Src) src, typename boost::remove_reference::type::iterator first, typename boost::remove_reference::type::iterator last, typename enable_if_not_key_based::type=0) { dst.splice(dst.end(),final_forward(src),first,last); } template void test_merge_same(Dst& dst,BOOST_FWD_REF(Src) src) { std::size_t n=dst.size(); merge(dst,boost::forward(src)); BOOST_TEST(dst.size()==n); BOOST_TEST(src.size()==n); } template bool find_address(Iterator first,Iterator last,const Value* x) { while(first!=last)if(&*first++==x)return true; return false; } template void test_merge_different( Dst& dst,BOOST_FWD_REF(Src) src,bool transferred_iters) { typedef typename boost::remove_reference:: type::iterator src_iterator; typedef typename boost::remove_reference:: type::value_type src_value_type; std::size_t n=dst.size(),m=src.size(); std::vector< std::pair > v; for(src_iterator first=src.begin(),last=src.end();first!=last;++first){ v.push_back(std::make_pair(first,&*first)); } merge(dst,boost::forward(src)); BOOST_TEST(dst.size()>=n && m>=src.size() && dst.size()-n==m-src.size()); for(std::size_t i=0;i void test_merge_same( Dst& dst,BOOST_FWD_REF(Src) src, typename boost::remove_reference::type::iterator i, bool key_based=is_key_based::value) { typedef typename Dst::iterator dst_iterator; std::size_t n=dst.size(); std::pair p=merge(dst,boost::forward(src),i); BOOST_TEST(dst.size()==n); BOOST_TEST(src.size()==n); BOOST_TEST(&*(p.first)==&*i && p.second); if(!key_based)BOOST_TEST(boost::next(p.first)==dst.end()); } template void test_merge_different( Dst& dst,BOOST_FWD_REF(Src) src, typename boost::remove_reference::type::iterator i, bool key_based=is_key_based::value) { typedef typename Dst::iterator dst_iterator; std::size_t n=dst.size(),m=src.size(); const typename Dst::value_type* pi=&*i; std::pair p=merge(dst,boost::forward(src),i); BOOST_TEST(dst.size()+src.size()==n+m); BOOST_TEST(p.second? &*(p.first)==pi: &*(p.first)!=pi && *(p.first)==*i); if(!key_based)BOOST_TEST(!p.second || boost::next(p.first)==dst.end()); } template void test_merge_same( Dst& dst,BOOST_FWD_REF(Src) src, typename boost::remove_reference::type::iterator first, typename boost::remove_reference::type::iterator last, bool key_based=is_key_based::value) { typedef typename Dst::iterator dst_iterator; typedef typename boost::remove_reference:: type::iterator src_iterator; typedef typename boost::remove_reference:: type::value_type src_value_type; std::size_t n=dst.size(),d=std::distance(first,last); std::vector v; for(src_iterator it=first;it!=last;++it)v.push_back(&*it); merge(dst,boost::forward(src),first,last); BOOST_TEST(dst.size()==n); BOOST_TEST(src.size()==n); if(!key_based){ dst_iterator it=boost::next(dst.begin(),dst.size()-d); for(std::size_t i=0;i void test_merge_different( Dst& dst,BOOST_FWD_REF(Src) src, typename boost::remove_reference::type::iterator first, typename boost::remove_reference::type::iterator last, bool key_based=is_key_based::value) { typedef typename Dst::iterator dst_iterator; typedef typename boost::remove_reference:: type::iterator src_iterator; typedef typename boost::remove_reference:: type::value_type src_value_type; std::size_t n=dst.size(), m=src.size(); std::vector v; for(src_iterator it=first;it!=last;++it)v.push_back(&*it); merge(dst,boost::forward(src),first,last); BOOST_TEST(dst.size()>=n && m>=src.size() && dst.size()-n==m-src.size()); if(!key_based){ for(dst_iterator it=boost::next(dst.begin(),n);it!=dst.end();++it){ BOOST_TEST(std::find(v.begin(),v.end(),&*it)!=v.end()); } } for(std::size_t i=0;i void test_merge_same(Dst& dst) { const Dst dst1=dst; { Dst dst2=dst1; test_merge_same( dst2.template get(),dst2.template get()); test_merge_same( dst2.template get(),boost::move(dst2.template get())); } { Dst dst2=dst1; test_merge_same( dst2.template get(),dst2.template get(), boost::next(dst2.template get().begin(),dst2.size()/2)); test_merge_same( dst2.template get(),boost::move(dst2.template get()), boost::next(dst2.template get().begin(),dst2.size()/2)); } { Dst dst2=dst1; test_merge_same( dst2.template get(),dst2.template get(), dst2.template get().begin(), boost::next(dst2.template get().begin(),dst2.size()/2)); test_merge_same( dst2.template get(),boost::move(dst2.template get()), dst2.template get().begin(), boost::next(dst2.template get().begin(),dst2.size()/2)); } } template void test_merge_different(Dst& dst,Src& src) { const Dst dst1=dst; const Src src1=src; const bool transferred_iters= boost::is_same< typename boost::multi_index::nth_index::type::iterator, typename boost::multi_index::nth_index::type::iterator>::value; { Dst dst2=dst1; Src src2=src1; test_merge_different( dst2.template get(),src2.template get(),transferred_iters); } { Dst dst2=dst1; Src src2=src1; test_merge_different( dst2.template get(),boost::move(src2.template get()), transferred_iters); } { Dst dst2=dst1; Src src2=src1; test_merge_different( dst2.template get(),src2.template get(), boost::next(src2.template get().begin(),src2.size()/2)); } { Dst dst2=dst1; Src src2=src1; test_merge_different( dst2.template get(),boost::move(src2.template get()), boost::next(src2.template get().begin(),src2.size()/2)); } { Dst dst2=dst1; Src src2=src1; test_merge_different( dst2.template get(),src2.template get(), src2.template get().begin(), boost::next(src2.template get().begin(),src2.size()/2)); } { Dst dst2=dst1; Src src2=src1; test_merge_different( dst2.template get(),boost::move(src2.template get()), src2.template get().begin(), boost::next(src2.template get().begin(),src2.size()/2)); } } template void test_merge(Dst& dst,Src& src) { test_merge_different(dst,src); } template void test_merge(Dst& dst,Dst& src) { if(&dst==&src)test_merge_same(dst); else test_merge_different(dst,src); } struct another_int_hash { std::size_t operator()(int x)const { return boost::hash()(x)*2; } }; void test_merge() { typedef multi_index_container< int, indexed_by< ordered_non_unique >, hashed_non_unique >, random_access<>, sequenced<>, ranked_non_unique > > > container1; typedef multi_index_container< int, indexed_by< ordered_unique< identity, std::greater >, hashed_unique< identity, another_int_hash >, random_access<>, sequenced<>, ranked_unique< identity, std::greater > > > container2; container1 c1; container2 c2; for(int i=0;i<10;++i){ c1.insert(i); c2.insert(2*i); } test_merge<0,1>(c1,c1); test_merge<1,2>(c1,c1); test_merge<2,3>(c1,c1); test_merge<3,4>(c1,c1); test_merge<4,0>(c1,c1); test_merge<0,3>(c2,c2); test_merge<1,4>(c2,c2); test_merge<4,2>(c2,c2); test_merge<0,1>(c1,c2); test_merge<1,2>(c1,c2); test_merge<2,3>(c1,c2); test_merge<3,4>(c1,c2); test_merge<4,0>(c1,c2); test_merge<0,3>(c2,c1); test_merge<1,4>(c2,c1); test_merge<4,2>(c2,c1); } void test_node_handling() { test_node_handle(); test_transfer(); test_merge(); }