From dc480499a252da210eeb4577e2cf558db02c0f35 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Tue, 10 Aug 2021 19:24:48 +0200 Subject: [PATCH] added partial merge functionality to key-based indices plus fixed a compile-time error with legacy splice code when applied to different-type indices --- .../boost/multi_index/detail/index_base.hpp | 3 - .../multi_index/detail/ord_index_impl.hpp | 70 +++++- include/boost/multi_index/hashed_index.hpp | 68 ++++- .../boost/multi_index/random_access_index.hpp | 27 +- include/boost/multi_index/sequenced_index.hpp | 27 +- test/test_node_handling.cpp | 238 +++++++++++++++++- 6 files changed, 359 insertions(+), 74 deletions(-) diff --git a/include/boost/multi_index/detail/index_base.hpp b/include/boost/multi_index/detail/index_base.hpp index bd31558..973c2c9 100644 --- a/include/boost/multi_index/detail/index_base.hpp +++ b/include/boost/multi_index/detail/index_base.hpp @@ -331,9 +331,6 @@ protected: void final_delete_all_nodes_(){final().delete_all_nodes_();} void final_clear_(){final().clear_();} - template - void final_transfer_range_(Index& x) - {final_transfer_range_(x,x.begin(),x.end());} template void final_transfer_range_( Index& x, diff --git a/include/boost/multi_index/detail/ord_index_impl.hpp b/include/boost/multi_index/detail/ord_index_impl.hpp index 1a5b40c..9afe20f 100644 --- a/include/boost/multi_index/detail/ord_index_impl.hpp +++ b/include/boost/multi_index/detail/ord_index_impl.hpp @@ -238,11 +238,9 @@ protected: typedef typename call_traits< key_type>::param_type key_param_type; - /* Needed to avoid commas in BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL - * expansion. - */ + /* needed to avoid commas in some macros */ - typedef std::pair emplace_return_type; + typedef std::pair pair_return_type; public: @@ -305,7 +303,7 @@ public: /* modifiers */ BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - emplace_return_type,emplace,emplace_impl) + pair_return_type,emplace,emplace_impl) BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( iterator,emplace_hint,emplace_hint_impl,iterator,position) @@ -540,17 +538,69 @@ public: BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(ordered_index_impl,Index,void) merge(Index& x) { - BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x); - BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; - if(x.end().get_node()!=this->header()){ /* different containers */ - this->final_transfer_range_(x); - } + merge(x,x.begin(),x.end()); } template BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(ordered_index_impl,Index,void) merge(BOOST_RV_REF(Index) x){merge(static_cast(x));} + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE( + ordered_index_impl,Index,pair_return_type) + merge(Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(i); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x); + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + if(x.end().get_node()==this->header()){ /* same container */ + return std::pair( + make_iterator(static_cast(i.get_node())),true); + } + else{ + std::pair p=this->final_transfer_( + x,static_cast(i.get_node())); + return std::pair(make_iterator(p.first),p.second); + } + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE( + ordered_index_impl,Index,pair_return_type) + merge(BOOST_RV_REF(Index) x,BOOST_DEDUCED_TYPENAME Index::iterator i) + { + return merge(static_cast(x),i); + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(ordered_index_impl,Index,void) + merge( + Index& x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(first); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(last); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(first,x); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x); + BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last); + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + if(x.end().get_node()!=this->header()){ /* different containers */ + this->final_transfer_range_(x,first,last); + } + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(ordered_index_impl,Index,void) + merge( + BOOST_RV_REF(Index) x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last) + { + merge(static_cast(x),first,last); + } + /* observers */ key_from_value key_extractor()const{return key;} diff --git a/include/boost/multi_index/hashed_index.hpp b/include/boost/multi_index/hashed_index.hpp index ff84151..aa40dfb 100644 --- a/include/boost/multi_index/hashed_index.hpp +++ b/include/boost/multi_index/hashed_index.hpp @@ -206,11 +206,9 @@ private: typedef typename call_traits< key_type>::param_type key_param_type; - /* Needed to avoid commas in BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL - * expansion. - */ + /* needed to avoid commas in some macros */ - typedef std::pair emplace_return_type; + typedef std::pair pair_return_type; public: @@ -280,7 +278,7 @@ public: /* modifiers */ BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - emplace_return_type,emplace,emplace_impl) + pair_return_type,emplace,emplace_impl) BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( iterator,emplace_hint,emplace_hint_impl,iterator,position) @@ -519,17 +517,67 @@ public: BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,void) merge(Index& x) { - BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x); - BOOST_MULTI_INDEX_HASHED_INDEX_CHECK_INVARIANT; - if(x.end().get_node()!=this->header()){ /* different containers */ - this->final_transfer_range_(x); - } + merge(x,x.begin(),x.end()); } template BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,void) merge(BOOST_RV_REF(Index) x){merge(static_cast(x));} + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,pair_return_type) + merge(Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(i); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x); + BOOST_MULTI_INDEX_HASHED_INDEX_CHECK_INVARIANT; + if(x.end().get_node()==this->header()){ /* same container */ + return std::pair( + make_iterator(static_cast(i.get_node())),true); + } + else{ + std::pair p=this->final_transfer_( + x,static_cast(i.get_node())); + return std::pair(make_iterator(p.first),p.second); + } + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,pair_return_type) + merge(BOOST_RV_REF(Index) x,BOOST_DEDUCED_TYPENAME Index::iterator i) + { + return merge(static_cast(x),i); + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,void) + merge( + Index& x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(first); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(last); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(first,x); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x); + BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last); + BOOST_MULTI_INDEX_HASHED_INDEX_CHECK_INVARIANT; + if(x.end().get_node()!=this->header()){ /* different containers */ + this->final_transfer_range_(x,first,last); + } + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,void) + merge( + BOOST_RV_REF(Index) x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last) + { + merge(static_cast(x),first,last); + } + /* observers */ key_from_value key_extractor()const{return key;} diff --git a/include/boost/multi_index/random_access_index.hpp b/include/boost/multi_index/random_access_index.hpp index d6f6836..cf0074d 100644 --- a/include/boost/multi_index/random_access_index.hpp +++ b/include/boost/multi_index/random_access_index.hpp @@ -184,7 +184,7 @@ private: /* needed to avoid commas in some macros */ - typedef std::pair insertion_return_type; + typedef std::pair pair_return_type; public: @@ -330,7 +330,7 @@ public: /* modifiers */ BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - insertion_return_type,emplace_front,emplace_front_impl) + pair_return_type,emplace_front,emplace_front_impl) std::pair push_front(const value_type& x) {return insert(begin(),x);} @@ -339,7 +339,7 @@ public: void pop_front(){erase(begin());} BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - insertion_return_type,emplace_back,emplace_back_impl) + pair_return_type,emplace_back,emplace_back_impl) std::pair push_back(const value_type& x) {return insert(end(),x);} @@ -348,7 +348,7 @@ public: void pop_back(){erase(--end());} BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( - insertion_return_type,emplace,emplace_impl,iterator,position) + pair_return_type,emplace,emplace_impl,iterator,position) std::pair insert(iterator position,const value_type& x) { @@ -557,7 +557,7 @@ public: template BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE( - random_access_index,Index,insertion_return_type) + random_access_index,Index,pair_return_type) splice( iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i) { @@ -583,7 +583,7 @@ public: template BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE( - random_access_index,Index,insertion_return_type) + random_access_index,Index,pair_return_type) splice( iterator position,BOOST_RV_REF(Index) x, BOOST_DEDUCED_TYPENAME Index::iterator i) @@ -1121,20 +1121,7 @@ private: /* backwards compatibility with old, non-transfer-based splice */ std::pair p=insert(position,*i); - if(p.second){ - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - /* MSVC++ 6.0 optimizer has a hard time with safe mode, and the - * following workaround is needed. Left it for all compilers as it - * does no harm. - */ - - i.detach(); - x.erase(x.make_iterator(i.get_node())); -#else - x.erase(i); -#endif - } + if(p.second)x.erase(i); return std::pair( static_cast(p.first.get_node()),p.second); } diff --git a/include/boost/multi_index/sequenced_index.hpp b/include/boost/multi_index/sequenced_index.hpp index 96b172f..57414c5 100644 --- a/include/boost/multi_index/sequenced_index.hpp +++ b/include/boost/multi_index/sequenced_index.hpp @@ -170,7 +170,7 @@ private: /* needed to avoid commas in some macros */ - typedef std::pair insertion_return_type; + typedef std::pair pair_return_type; public: @@ -292,7 +292,7 @@ public: /* modifiers */ BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - insertion_return_type,emplace_front,emplace_front_impl) + pair_return_type,emplace_front,emplace_front_impl) std::pair push_front(const value_type& x) {return insert(begin(),x);} @@ -301,7 +301,7 @@ public: void pop_front(){erase(begin());} BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - insertion_return_type,emplace_back,emplace_back_impl) + pair_return_type,emplace_back,emplace_back_impl) std::pair push_back(const value_type& x) {return insert(end(),x);} @@ -310,7 +310,7 @@ public: void pop_back(){erase(--end());} BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( - insertion_return_type,emplace,emplace_impl,iterator,position) + pair_return_type,emplace,emplace_impl,iterator,position) std::pair insert(iterator position,const value_type& x) { @@ -508,7 +508,7 @@ public: template BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE( - sequenced_index,Index,insertion_return_type) + sequenced_index,Index,pair_return_type) splice( iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i) { @@ -534,7 +534,7 @@ public: template BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE( - sequenced_index,Index,insertion_return_type) + sequenced_index,Index,pair_return_type) splice( iterator position,BOOST_RV_REF(Index) x, BOOST_DEDUCED_TYPENAME Index::iterator i) @@ -1034,20 +1034,7 @@ private: /* backwards compatibility with old, non-transfer-based splice */ std::pair p=insert(position,*i); - if(p.second){ - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - /* MSVC++ 6.0 optimizer has a hard time with safe mode, and the - * following workaround is needed. Left it for all compilers as it - * does no harm. - */ - - i.detach(); - x.erase(x.make_iterator(i.get_node())); -#else - x.erase(i); -#endif - } + if(p.second)x.erase(i); return std::pair( static_cast(p.first.get_node()),p.second); } diff --git a/test/test_node_handling.cpp b/test/test_node_handling.cpp index 84fcef8..f356e93 100644 --- a/test/test_node_handling.cpp +++ b/test/test_node_handling.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include "count_allocator.hpp" @@ -454,6 +455,44 @@ void merge( 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) { @@ -480,27 +519,160 @@ void test_merge_different(Dst& dst,BOOST_FWD_REF(Src) src) } merge(dst,boost::forward(src)); - BOOST_TEST(dst.size()+src.size()==n+m); + 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 +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, + 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(), + 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 && 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) { - Dst dst1=dst; - test_merge_same( - dst1.template get(),dst1.template get()); - test_merge_same( - dst1.template get(),boost::move(dst1.template get())); + 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) { - Dst dst1=dst; - Src src1=src; + const Dst dst1=dst; + const Src src1=src; { Dst dst2=dst1; Src src2=src1; @@ -513,6 +685,36 @@ void test_merge_different(Dst& dst,Src& src) test_merge_different( dst2.template get(),boost::move(src2.template get())); } + { + 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 @@ -528,6 +730,14 @@ void test_merge(Dst& dst,Dst& src) 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< @@ -544,8 +754,14 @@ void test_merge() typedef multi_index_container< int, indexed_by< - ordered_unique >, - hashed_unique >, + ordered_unique< + identity, + std::greater + >, + hashed_unique< + identity, + another_int_hash + >, random_access<>, sequenced<>, ranked_unique< @@ -557,7 +773,7 @@ void test_merge() container1 c1; container2 c2; - for(int i=0;i<5;++i){ + for(int i=0;i<10;++i){ c1.insert(i); c2.insert(2*i); }