diff --git a/appveyor.yml b/appveyor.yml index f01df86..f485522 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -# Copyright 2003-2021 Joaquín M López Muñoz. +# Copyright 2003-2021 Joaquín M López Muñoz. # 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) @@ -34,4 +34,4 @@ build: off test_script: - if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD% - - b2 address-model=32 libs/multi_index/test toolset=%TOOLSET% %CXXSTD% \ No newline at end of file + - b2 address-model=32 libs/multi_index/test toolset=%TOOLSET% %CXXSTD% diff --git a/doc/reference/hash_indices.html b/doc/reference/hash_indices.html index c3aeb83..1e1c838 100644 --- a/doc/reference/hash_indices.html +++ b/doc/reference/hash_indices.html @@ -308,6 +308,16 @@ requirements. void clear()noexcept; void swap(index class name& x); + template<typename Index> void merge(Index&& x); + template<typename Index> + std::pair<iterator,bool> merge( + Index&& x,typename std::remove_reference_t<Index>::const_iterator i); + template<typename Index> + void merge( + Index&& x, + typename std::remove_reference_t<Index>::const_iterator first, + typename std::remove_reference_t<Index>::const_iterator last); + // observers: key_from_value key_extractor()const; @@ -474,7 +484,9 @@ local_iterator
const_local_iterator
-These types are forward iterators. +These types are forward iterators. They depend only on node_type and whether +the index is unique or not (this implies that, for instance, iterators to elements transferred +from a unique index to a non-unique one will become invalid).

Constructors, copy and assignment

@@ -925,6 +937,89 @@ with mod' and back defined in such a way that key is the internal KeyFromValue object of the index. +template<typename Index> void merge(Index&& x); + +
+Requires: x is a non-const reference to an index of a +node-compatible +multi_index_container. get_allocator()==x.get_allocator().
+Effects: +
+merge(x,x.begin(),x.end());
+
+
+ + +template<typename Index> std::pair<iterator,bool> merge(
+  Index&& x,typename std::remove_reference_t<Index>::const_iterator i); +
+ +
+Requires: x is a non-const reference to an index of a +node-compatible +multi_index_container. get_allocator()==x.get_allocator(). +i is a valid dereferenceable iterator of x.
+Effects: Does nothing if the source and destination containers are the same; +otherwise, transfers the node of the element referred to by i into the +multi_index_container to which the destination index belongs if + +Note that no element is copied or destroyed in the process.
+Postconditions: If transfer succeeds, for any index in the source container +having the same iterator/const_iterator types as the corresponding +index in the destination container, iterators referring to *i +remain valid and behave as iterators of the destination index.
+Returns: The return value is a pair p. p.second +is true if and only if transfer took place or the source and destination +containers are the same. If p.second is true, +p.first points to *i; otherwise, p.first +points to an element that caused the insertion to be banned. Note that more than +one element can be causing insertion not to be allowed.
+Complexity: If the source and destination containers are the same, +constant; otherwise, O(I(n)+D(x.size())).
+Exception safety: If the source and destination containers are the same, +nothrow; otherwise strong. +
+ + +template<typename Index> void merge(
+  Index&& x,
+  typename std::remove_reference_t<Index>::const_iterator first,
+  typename std::remove_reference_t<Index>::const_iterator last); +
+ +
+Requires: x is a non-const reference to an index of a +node-compatible +multi_index_container. get_allocator()==x.get_allocator(). +[first,last) is a valid range of x.
+Effects: Does nothing if the source and destination containers are the same; +otherwise, for each node in [first,last), in this order, +the node is transferred to the multi_index_container to which the +destination index belongs if + +Note that no element is copied or destroyed in the process.
+Postconditions: For any index in the source container having the same +iterator/const_iterator types as the corresponding +index in the destination container, iterators referring to the transferred elements +remain valid and behave as iterators of the destination index.
+Complexity: If the source and destination containers are the same, +constant; otherwise, O(m*(I(n+m)+D(x.size()))), where +m is the number of elements in [first, +last).
+Exception safety: If the source and destination containers are the same, +nothrow; otherwise basic. +
+

Observers

Apart from standard hash_function and key_eq, @@ -1275,7 +1370,7 @@ Sequenced indices
-

Revised July 29th 2021

+

Revised August 16th 2021

© Copyright 2003-2021 Joaquín M López Muñoz. Distributed under the Boost Software diff --git a/doc/reference/ord_indices.html b/doc/reference/ord_indices.html index 34600f2..af5b491 100644 --- a/doc/reference/ord_indices.html +++ b/doc/reference/ord_indices.html @@ -42,6 +42,7 @@ Ranked indices

@@ -282,7 +283,7 @@ determining whether a hashed index is preferred over an ordered one.

-Hashed indices replicate the interface as std::unordered_set and +Hashed indices replicate the interface of std::unordered_set and std::unordered_multiset, with only minor differences where required by the general constraints of multi_index_containers, and provide additional useful capabilities like in-place updating of elements. @@ -774,6 +775,113 @@ in scenarios where access via iterators is not suitable or desireable:

+

Node handling operations

+ +

+Using direct node manipulation, elements can be passed between +multi_index_containers without actually copying them: +

+ +
+// move an employee to the retiree archive
+void move_to_retirement(int ssnumber,employee_set& es,employee_set& archive)
+{
+  // assume employee_set has an index on SS number(not shown before)
+  // extract the employee with given SS number to a node handle
+  employee_set_by_ssn::node_type node=es.get<ssn>().extract(ssnumber);
+
+  if(!node.empty()){ // employee found
+    // re-insert into archive (note the use of std::move)
+    archive.insert(std::move(node));
+  }
+}
+
+ +

+In the example, the internal node is transferred as-is from es to archive, +which is more efficient than erasing from the source and recreating in destination. +node_type is a move-only class used to pass nodes around, and its interface follows +that of the homonym type +for C++ associative containers (set containers version). Boost.MultiIndex provides node extraction +and insertion operations for all index types, including sequenced ones (by contrast, +std::list does not have such features): +

+ +
+multi_index_container<
+  int,
+  indexed_by<
+    sequenced<>,
+    ordered_unique<identity<int> >
+  >
+> src;
+
+multi_index_container<
+  int,
+  indexed_by<
+    sequenced<>,
+    ordered_non_unique<identity<int>, std::greater<int> >
+  >
+> dst;
+
+...
+
+// transfer even numbers from src to dst
+for(auto first=src.begin(),last=src.end();first!=last;){
+  if(*first%2==0) dst.insert(dst.end(),src.extract(first++));
+  else            ++first;
+}
+
+ +

+Note that src and dst are of different types, +yet transfer is possible. Two multi_index_containers are +node-compatible (that is, they use the same node_type) if +they have the same element and allocator types and their respective indices match +one by one without regard to whether they are unique or non-unique or to +their particular configuration parameters: they are both ordered, or +both sequenced, etc. +

+ +

+Alternatively, direct node transfer between two containers can be done without +keeping intervening node_types thanks to merge (key-based +indices) and splice (non key-based indices). +

+ +
+// move older employees to retirement
+void move_to_retirement_by_age(
+  int max_age,employee_set& es,employee_set& archive)
+{
+  // assume employee_set has an index on age (not shown before)
+  employee_set_by_age& ea=es.get<age>();
+
+  // archive employees with age>max_age
+  archive.merge(ea,ea.upper_bound(max_age),ea.end());
+}
+
+...
+
+// transfer even numbers from src to dst
+for(auto first=src.begin(),last=src.end();first!=last;){
+  if(*first%2==0) dst.splice(dst.end(),src,first++);
+  else            ++first;
+}
+
+ +

+There are overloads of merge/splice for transferring a single element, +a range between two iterators and an entire container: for further details, consult +for instance the reference for ordered indices and for +sequenced indices +(the rest of indices provide one interface or the other). +Please note that sequenced and random access indices do also have an operation called merge, +but this follows the specification of std::list::merge, which has a somewhat +different behavior (source and destination are required to be ordered by the same criterion). This is +a rather confusing naming issue that Boost.MultiIndex simply inherits from the C++ standard. +

+

Ordered indices node compression

@@ -821,7 +929,7 @@ Key extraction
-

Revised July 29th 2021

+

Revised August 16th 2021

© Copyright 2003-2021 Joaquín M López Muñoz. Distributed under the Boost Software diff --git a/include/boost/multi_index/detail/any_container_view.hpp b/include/boost/multi_index/detail/any_container_view.hpp new file mode 100644 index 0000000..6432b35 --- /dev/null +++ b/include/boost/multi_index/detail/any_container_view.hpp @@ -0,0 +1,76 @@ +/* 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. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_ANY_CONTAINER_VIEW_HPP +#define BOOST_MULTI_INDEX_DETAIL_ANY_CONTAINER_VIEW_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* type-erased, non-owning view over a ConstIterator-container's range */ + +template +class any_container_view +{ +public: + template + any_container_view(const Container& x):px(&x),pt(vtable_for()){} + + const void* container()const{return px;} + ConstIterator begin()const{return pt->begin(px);} + ConstIterator end()const{return pt->end(px);} + +private: + struct vtable + { + ConstIterator (*begin)(const void*); + ConstIterator (*end)(const void*); + }; + + template + static ConstIterator begin_for(const void* px) + { + return static_cast(px)->begin(); + } + + template + static ConstIterator end_for(const void* px) + { + return static_cast(px)->end(); + } + + template + vtable* vtable_for() + { + static vtable v= + { + &begin_for, + &end_for + }; + + return &v; + } + + const void* px; + vtable* pt; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/index_access_sequence.hpp b/include/boost/multi_index/detail/index_access_sequence.hpp new file mode 100644 index 0000000..6539dee --- /dev/null +++ b/include/boost/multi_index/detail/index_access_sequence.hpp @@ -0,0 +1,74 @@ +/* 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. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_INDEX_ACCESS_SEQUENCE_HPP +#define BOOST_MULTI_INDEX_DETAIL_INDEX_ACCESS_SEQUENCE_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* Successive access to the indices of a multi_index_container. Used as dst in + * backbone function extract_(x,dst) to retrieve the destination indices + * where iterators referring to x must be transferred to (in merging operations). + */ + +template +struct index_access_sequence; + +struct index_access_sequence_terminal +{ + index_access_sequence_terminal(void*){} +}; + +template +struct index_access_sequence_normal +{ + MultiIndexContainer* p; + + index_access_sequence_normal(MultiIndexContainer* p_):p(p_){} + + typename nth_index::type& + get(){return p->template get();} + + index_access_sequence + next(){return index_access_sequence(p);} +}; + +template +struct index_access_sequence_base: + mpl::if_c< + N::type::value, + index_access_sequence_normal, + index_access_sequence_terminal + > +{}; + +template +struct index_access_sequence: + index_access_sequence_base::type +{ + typedef typename index_access_sequence_base< + MultiIndexContainer,N>::type super; + + index_access_sequence(MultiIndexContainer* p):super(p){} +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/index_base.hpp b/include/boost/multi_index/detail/index_base.hpp index 22cde3a..ede5a30 100644 --- a/include/boost/multi_index/detail/index_base.hpp +++ b/include/boost/multi_index/detail/index_base.hpp @@ -1,4 +1,4 @@ -/* Copyright 2003-2020 Joaquin M Lopez Munoz. +/* 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) @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -134,6 +135,15 @@ protected: return x; } + template + final_node_type* insert_( + const value_type&,final_node_type*& x,MultiIndexContainer* p) + { + p->final_extract_for_transfer_( + x,index_access_sequence(&final())); + return x; + } + final_node_type* insert_( const value_type& v,index_node_type*,final_node_type*& x,lvalue_tag) { @@ -152,7 +162,8 @@ protected: return x; } - void extract_(index_node_type*){} + template + void extract_(index_node_type*,Dst){} void clear_(){} @@ -203,6 +214,10 @@ protected: final_type& final(){return *static_cast(this);} const final_type& final()const{return *static_cast(this);} + template + static typename Index::final_type& final(Index& x) /* cross-index access */ + {return static_cast(x);} + final_node_type* final_header()const{return final().header();} bool final_empty_()const{return final().empty_();} @@ -222,6 +237,10 @@ protected: std::pair final_insert_nh_(final_node_handle_type& nh) {return final().insert_nh_(nh);} + template + std::pair final_transfer_(Index& x,final_node_type* n) + {return final().transfer_(x,n);} + template std::pair final_emplace_( BOOST_MULTI_INDEX_FUNCTION_PARAM_PACK) @@ -260,12 +279,25 @@ protected: return final().extract_(x); } + template + void final_extract_for_transfer_(final_node_type* x,Dst dst) + { + final().extract_for_transfer_(x,dst); + } + void final_erase_(final_node_type* x){final().erase_(x);} void final_delete_node_(final_node_type* x){final().delete_node_(x);} void final_delete_all_nodes_(){final().delete_all_nodes_();} void final_clear_(){final().clear_();} + template + void final_transfer_range_( + Index& x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last) + {final().transfer_range_(x,first,last);} + void final_swap_(final_type& x){final().swap_(x);} bool final_replace_( diff --git a/include/boost/multi_index/detail/invalidate_iterators.hpp b/include/boost/multi_index/detail/invalidate_iterators.hpp new file mode 100644 index 0000000..8d6ed85 --- /dev/null +++ b/include/boost/multi_index/detail/invalidate_iterators.hpp @@ -0,0 +1,44 @@ +/* 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. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_INVALIDATE_ITERATORS_HPP +#define BOOST_MULTI_INDEX_DETAIL_INVALIDATE_ITERATORS_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* invalidate_iterators mimics the interface of index_access_sequence (see + * index_access_sequence.hpp) but returns dummy indices whose iterator type + * won't ever match those of the source: the net effect is that safe iterator + * transfer resolves to iterator invalidation, so backbone function invocation + * extract_(x,invalidate_iterators()) is used in extraction scenarios other + * than merging. + */ + +struct invalidate_iterators +{ + typedef void iterator; + + invalidate_iterators& get(){return *this;} + invalidate_iterators& next(){return *this;} +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/node_handle.hpp b/include/boost/multi_index/detail/node_handle.hpp index 3d5ff19..bd24274 100644 --- a/include/boost/multi_index/detail/node_handle.hpp +++ b/include/boost/multi_index/detail/node_handle.hpp @@ -1,4 +1,4 @@ -/* Copyright 2003-2020 Joaquin M Lopez Munoz. +/* 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) @@ -25,6 +25,12 @@ #include #include +#if !defined(BOOST_NO_SFINAE) +#include +#include +#include +#endif + namespace boost{ namespace multi_index{ @@ -235,6 +241,23 @@ private: BOOST_MOVABLE_BUT_NOT_COPYABLE(insert_return_type) }; +/* utility for SFINAEing merge and related operations */ + +#if !defined(BOOST_NO_SFINAE) + +#define BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(Dst,Src,T) \ +typename enable_if_c< \ + !is_const< Dst >::value&&!is_const< Src >::value&& \ + is_same::value, \ + T \ +>::type + +#else + +#define BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(Dst,Src,T) T + +#endif + } /* namespace multi_index::detail */ } /* namespace multi_index */ diff --git a/include/boost/multi_index/detail/ord_index_impl.hpp b/include/boost/multi_index/detail/ord_index_impl.hpp index c37b52f..21e5b97 100644 --- a/include/boost/multi_index/detail/ord_index_impl.hpp +++ b/include/boost/multi_index/detail/ord_index_impl.hpp @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -127,13 +128,6 @@ template< > class ordered_index_impl: BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - ,public safe_mode::safe_container< - ordered_index_impl< - KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy> > -#endif - { #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ BOOST_WORKAROUND(__MWERKS__,<=0x3003) @@ -145,6 +139,12 @@ class ordered_index_impl: #pragma parse_mfunc_templ off #endif +#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) + /* cross-index access */ + + template friend class index_base; +#endif + typedef typename SuperMeta::type super; protected: @@ -171,8 +171,7 @@ public: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) typedef safe_mode::safe_iterator< - bidir_node_iterator, - ordered_index_impl> iterator; + bidir_node_iterator > iterator; #else typedef bidir_node_iterator iterator; #endif @@ -222,8 +221,7 @@ protected: protected: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - typedef safe_mode::safe_container< - ordered_index_impl> safe_super; + typedef safe_mode::safe_container safe_container; #endif typedef typename call_traits< @@ -231,11 +229,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: @@ -298,7 +294,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) @@ -529,6 +525,75 @@ public: this->final_clear_(); } + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(ordered_index_impl,Index,void) + merge(Index& 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_CHECK_EQUAL_ALLOCATORS(*this,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_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,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;} @@ -659,6 +724,11 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: super(args_list.get_tail(),al), key(tuples::get<0>(args_list.get_head())), comp_(tuples::get<1>(args_list.get_head())) + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + ,safe(*this) +#endif + { empty_initialize(); } @@ -667,13 +737,13 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: const ordered_index_impl< KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy>& x): super(x), - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super(), -#endif - key(x.key), comp_(x.comp_) + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + ,safe(*this) +#endif + { /* Copy ctor just takes the key and compare objects from x. The rest is * done in a subsequent call to copy_(). @@ -685,13 +755,13 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy>& x, do_not_copy_elements_tag): super(x,do_not_copy_elements_tag()), - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super(), -#endif - key(x.key), comp_(x.comp_) + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + ,safe(*this) +#endif + { empty_initialize(); } @@ -703,9 +773,9 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) iterator make_iterator(index_node_type* node) - {return iterator(node,this);} + {return iterator(node,&safe);} const_iterator make_iterator(index_node_type* node)const - {return const_iterator(node,const_cast(this));} + {return const_iterator(node,const_cast(&safe));} #else iterator make_iterator(index_node_type* node){return iterator(node);} const_iterator make_iterator(index_node_type* node)const @@ -809,14 +879,15 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: return res; } - void extract_(index_node_type* x) + template + void extract_(index_node_type* x,Dst dst) { node_impl_type::rebalance_for_extract( x->impl(),header()->parent(),header()->left(),header()->right()); - super::extract_(x); + super::extract_(x,dst.next()); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - detach_iterators(x); + transfer_iterators(dst.get(),x); #endif } @@ -831,7 +902,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: empty_initialize(); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::detach_dereferenceable_iterators(); + safe.detach_dereferenceable_iterators(); #endif } @@ -845,7 +916,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: adl_swap(comp_,x.comp_); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::swap(x); + safe.swap(x.safe); #endif super::swap_(x,swap_allocators); @@ -856,7 +927,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy>& x) { #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::swap(x); + safe.swap(x.safe); #endif super::swap_elements_(x); @@ -898,7 +969,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: b=in_place(x->value(),x,Category()); } BOOST_CATCH(...){ - extract_(x); + extract_(x,invalidate_iterators()); BOOST_RETHROW; } BOOST_CATCH_END @@ -908,7 +979,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: BOOST_TRY{ link_info inf; if(!link_point(key(x->value()),inf,Category())){ - super::extract_(x); + super::extract_(x,invalidate_iterators()); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) detach_iterators(x); @@ -918,7 +989,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: node_impl_type::link(x->impl(),inf.side,inf.pos,header()->impl()); } BOOST_CATCH(...){ - super::extract_(x); + super::extract_(x,invalidate_iterators()); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) detach_iterators(x); @@ -1272,6 +1343,13 @@ private: iterator it=make_iterator(x); safe_mode::detach_equivalent_iterators(it); } + + template + void transfer_iterators(Dst& dst,index_node_type* x) + { + iterator it=make_iterator(x); + safe_mode::transfer_equivalent_iterators(dst,it); + } #endif template @@ -1449,6 +1527,10 @@ protected: /* for the benefit of AugmentPolicy::augmented_interface */ key_from_value key; key_compare comp_; +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + safe_container safe; +#endif + #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ BOOST_WORKAROUND(__MWERKS__,<=0x3003) #pragma parse_mfunc_templ reset diff --git a/include/boost/multi_index/detail/rnd_index_node.hpp b/include/boost/multi_index/detail/rnd_index_node.hpp index 782fdd8..9f76eed 100644 --- a/include/boost/multi_index/detail/rnd_index_node.hpp +++ b/include/boost/multi_index/detail/rnd_index_node.hpp @@ -1,4 +1,4 @@ -/* Copyright 2003-2018 Joaquin M Lopez Munoz. +/* 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) @@ -169,6 +169,25 @@ struct random_access_index_node_impl } } + static ptr_pointer gather_nulls( + ptr_pointer pbegin,ptr_pointer pend,ptr_pointer x) + { + for(ptr_pointer p=pbegin;p!=x;++p){ + if(*p){ + *pbegin=*p; + (*pbegin)->up()=pbegin; + ++pbegin; + } + } + for(ptr_pointer p=pend;p!=x;){ + if(*--p){ + *--pend=*p; + (*pend)->up()=pend; + } + } + return pbegin; + } + private: ptr_pointer up_; }; diff --git a/include/boost/multi_index/detail/safe_mode.hpp b/include/boost/multi_index/detail/safe_mode.hpp index b5eec31..afc1332 100644 --- a/include/boost/multi_index/detail/safe_mode.hpp +++ b/include/boost/multi_index/detail/safe_mode.hpp @@ -1,4 +1,4 @@ -/* Copyright 2003-2020 Joaquin M Lopez Munoz. +/* 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) @@ -15,9 +15,9 @@ /* Safe mode machinery, in the spirit of Cay Hortmann's "Safe STL" * (http://www.horstmann.com/safestl.html). - * In this mode, containers of type Container are derived from - * safe_container, and their corresponding iterators - * are wrapped with safe_iterator. These classes provide + * In this mode, containers have to redefine their iterators as + * safe_iterator and keep a tracking object member of + * type safe_container >. These classes provide * an internal record of which iterators are at a given moment associated * to a given container, and properly mark the iterators as invalid * when the container gets destroyed. @@ -80,6 +80,11 @@ safe_mode::check_is_owner(it,cont), \ safe_mode::not_owner); +#define BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,cont) \ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \ + safe_mode::check_belongs_in_some_index(it,cont), \ + safe_mode::not_owner); + #define BOOST_MULTI_INDEX_CHECK_SAME_OWNER(it0,it1) \ BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \ safe_mode::check_same_owner(it0,it1), \ @@ -113,9 +118,12 @@ #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) #include /* keep it first to prevent nasty warns in MSVC */ #include +#include #include +#include #include #include +#include #include #if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION) @@ -125,6 +133,7 @@ #if defined(BOOST_HAS_THREADS) #include +#include #endif namespace boost{ @@ -162,18 +171,29 @@ inline bool check_decrementable_iterator(const Iterator& it) return (it.valid()&&it!=it.owner()->begin())||it.unchecked(); } -template +template inline bool check_is_owner( - const Iterator& it,const typename Iterator::container_type& cont) + const Iterator& it,const Container& cont) { - return (it.valid()&&it.owner()==&cont)||it.unchecked(); + return (it.valid()&& + it.owner()->container()==cont.end().owner()->container()) + ||it.unchecked(); } +template +inline bool check_belongs_in_some_index( + const Iterator& it,const MultiIndexContainer& cont) +{ + return (it.valid()&&it.owner()->end().get_node()==cont.end().get_node()) + ||it.unchecked(); +} + template inline bool check_same_owner(const Iterator& it0,const Iterator& it1) { - return (it0.valid()&&it1.valid()&&it0.owner()==it1.owner())|| - it0.unchecked()||it1.unchecked(); + return (it0.valid()&&it1.valid()&& + it0.owner()->container()==it1.owner()->container()) + ||it0.unchecked()||it1.unchecked(); } template @@ -217,6 +237,28 @@ inline bool check_outside_range( return true; } +template +inline bool check_outside_range( + const Iterator1& it,const Iterator2& it0,const Iterator2& it1) +{ + if(it.valid()&&it!=it.owner()->end()&&it0.valid()){ + Iterator2 last=it0.owner()->end(); + bool found=false; + + Iterator2 first=it0; + for(;first!=last;++first){ + if(first==it1)break; + + /* crucial that this check goes after previous break */ + + if(boost::addressof(*first)==boost::addressof(*it))found=true; + } + if(first!=it1)return false; + return !found; + } + return true; +} + template inline bool check_in_bounds(const Iterator& it,Difference n) { @@ -233,6 +275,12 @@ inline bool check_different_container( return &cont0!=&cont1; } +template +inline bool check_different_container(const Container1&,const Container2&) +{ + return true; +} + template inline bool check_equal_allocators( const Container0& cont0,const Container1& cont1) @@ -240,42 +288,20 @@ inline bool check_equal_allocators( return cont0.get_allocator()==cont1.get_allocator(); } -/* Invalidates all iterators equivalent to that given. Safe containers - * must call this when deleting elements: the safe mode framework cannot - * perform this operation automatically without outside help. - */ +/* fwd decls */ -template -inline void detach_equivalent_iterators(Iterator& it) -{ - if(it.valid()){ - { -#if defined(BOOST_HAS_THREADS) - boost::detail::lightweight_mutex::scoped_lock lock(it.cont->mutex); -#endif +template class safe_container; +template void detach_equivalent_iterators(Iterator&); - Iterator *prev_,*next_; - for( - prev_=static_cast(&it.cont->header); - (next_=static_cast(prev_->next))!=0;){ - if(next_!=&it&&*next_==it){ - prev_->next=next_->next; - next_->cont=0; - } - else prev_=next_; - } - } - it.detach(); - } -} +namespace safe_mode_detail{ -template class safe_container; /* fwd decl. */ +/* fwd decls */ -} /* namespace multi_index::safe_mode */ - -namespace detail{ - -class safe_container_base; /* fwd decl. */ +class safe_container_base; +template +void transfer_equivalent_iterators(Dst&,Iterator,boost::true_type); +template +inline void transfer_equivalent_iterators(Dst&,Iterator&,boost::false_type); class safe_iterator_base { @@ -325,12 +351,16 @@ protected: const safe_container_base* owner()const{return cont;} BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS: - friend class safe_container_base; #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) - template friend class safe_mode::safe_container; - template friend - void safe_mode::detach_equivalent_iterators(Iterator&); + friend class safe_container_base; + template + friend class safe_mode::safe_container; + template + friend void safe_mode::detach_equivalent_iterators(Iterator&); + template + friend void safe_mode_detail::transfer_equivalent_iterators( + Dst&,Iterator,boost::true_type); #endif inline void attach(safe_container_base* cont_); @@ -346,11 +376,14 @@ public: safe_container_base(){} BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: - friend class safe_iterator_base; #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) - template friend - void safe_mode::detach_equivalent_iterators(Iterator&); + friend class safe_iterator_base; + template + friend void safe_mode::detach_equivalent_iterators(Iterator&); + template + friend void safe_mode_detail::transfer_equivalent_iterators( + Dst&,Iterator,boost::true_type); #endif ~safe_container_base() @@ -405,41 +438,36 @@ void safe_iterator_base::detach() } } -} /* namespace multi_index::detail */ - -namespace safe_mode{ +} /* namespace multi_index::safe_mode::safe_mode_detail */ /* In order to enable safe mode on a container: - * - The container must derive from safe_container, + * - The container must keep a member of type safe_container, * - iterators must be generated via safe_iterator, which adapts a - * preexistent unsafe iterator class. + * preexistent unsafe iterator class. safe_iterators are passed the + * address of the previous safe_container member at construction time. */ -template -class safe_container; - -template +template class safe_iterator: - public detail::iter_adaptor,Iterator>, - public detail::safe_iterator_base + public detail::iter_adaptor,Iterator>, + public safe_mode_detail::safe_iterator_base { typedef detail::iter_adaptor super; - typedef detail::safe_iterator_base safe_super; + typedef safe_mode_detail::safe_iterator_base safe_super; public: - typedef Container container_type; typedef typename Iterator::reference reference; typedef typename Iterator::difference_type difference_type; safe_iterator(){} - explicit safe_iterator(safe_container* cont_): + explicit safe_iterator(safe_container* cont_): safe_super(cont_){} template - safe_iterator(const T0& t0,safe_container* cont_): + safe_iterator(const T0& t0,safe_container* cont_): super(Iterator(t0)),safe_super(cont_){} template safe_iterator( - const T0& t0,const T1& t1,safe_container* cont_): + const T0& t0,const T1& t1,safe_container* cont_): super(Iterator(t0,t1)),safe_super(cont_){} safe_iterator(const safe_iterator& x):super(x),safe_super(x){} @@ -451,12 +479,11 @@ public: return *this; } - const container_type* owner()const + const safe_container* owner()const { return - static_cast( - static_cast*>( - this->safe_super::owner())); + static_cast*>( + this->safe_super::owner()); } /* get_node is not to be used by the user */ @@ -547,21 +574,28 @@ private: #endif }; -template -class safe_container:public detail::safe_container_base +template +class safe_container:public safe_mode_detail::safe_container_base { - typedef detail::safe_container_base super; + typedef safe_mode_detail::safe_container_base super; + + detail::any_container_view view; public: + template + safe_container(const Container& c):view(c){} + + const void* container()const{return view.container();} + Iterator begin()const{return view.begin();} + Iterator end()const{return view.end();} + void detach_dereferenceable_iterators() { - typedef typename Container::iterator iterator; - - iterator end_=static_cast(this)->end(); - iterator *prev_,*next_; + Iterator end_=view.end(); + Iterator *prev_,*next_; for( - prev_=static_cast(&this->header); - (next_=static_cast(prev_->next))!=0;){ + prev_=static_cast(&this->header); + (next_=static_cast(prev_->next))!=0;){ if(*next_!=end_){ prev_->next=next_->next; next_->cont=0; @@ -570,21 +604,102 @@ public: } } - void swap(safe_container& x) + void swap(safe_container& x) { super::swap(x); } }; +/* Invalidates all iterators equivalent to that given. Safe containers + * must call this when deleting elements: the safe mode framework cannot + * perform this operation automatically without outside help. + */ + +template +inline void detach_equivalent_iterators(Iterator& it) +{ + if(it.valid()){ + { +#if defined(BOOST_HAS_THREADS) + boost::detail::lightweight_mutex::scoped_lock lock(it.cont->mutex); +#endif + + Iterator *prev_,*next_; + for( + prev_=static_cast(&it.cont->header); + (next_=static_cast(prev_->next))!=0;){ + if(next_!=&it&&*next_==it){ + prev_->next=next_->next; + next_->cont=0; + } + else prev_=next_; + } + } + it.detach(); + } +} + +/* Transfers iterators equivalent to that given to Dst, if that container has + * the same iterator type; otherwise, detaches them. + */ + +template +inline void transfer_equivalent_iterators(Dst& dst,Iterator& i) +{ + safe_mode_detail::transfer_equivalent_iterators( + dst,i,boost::is_same()); +} + +namespace safe_mode_detail{ + +template +inline void transfer_equivalent_iterators( + Dst& dst,Iterator it,boost::true_type /* same iterator type */) +{ + if(it.valid()){ + { + safe_container_base* cont_=dst.end().cont; + +#if defined(BOOST_HAS_THREADS) + detail::scoped_bilock + scoped_bilock(it.cont->mutex,cont_->mutex); +#endif + + Iterator *prev_,*next_; + for( + prev_=static_cast(&it.cont->header); + (next_=static_cast(prev_->next))!=0;){ + if(next_!=&it&&*next_==it){ + prev_->next=next_->next; + next_->cont=cont_; + next_->next=cont_->header.next; + cont_->header.next=next_; + } + else prev_=next_; + } + } + /* nothing to do with it, was passed by value and will die now */ + } +} + +template +inline void transfer_equivalent_iterators( + Dst&,Iterator& it,boost::false_type /* same iterator type */) +{ + detach_equivalent_iterators(it); +} + +} /* namespace multi_index::safe_mode::safe_mode_detail */ + } /* namespace multi_index::safe_mode */ } /* namespace multi_index */ #if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION) namespace serialization{ -template +template struct version< - boost::multi_index::safe_mode::safe_iterator + boost::multi_index::safe_mode::safe_iterator > { BOOST_STATIC_CONSTANT( diff --git a/include/boost/multi_index/detail/scoped_bilock.hpp b/include/boost/multi_index/detail/scoped_bilock.hpp new file mode 100644 index 0000000..880d316 --- /dev/null +++ b/include/boost/multi_index/detail/scoped_bilock.hpp @@ -0,0 +1,70 @@ +/* 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. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_SCOPED_BILOCK_HPP +#define BOOST_MULTI_INDEX_DETAIL_SCOPED_BILOCK_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +#include +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* Locks/unlocks two RAII-lockable mutexes taking care that locking is done in + * a deadlock-avoiding global order and no double locking happens when the two + * mutexes are the same. + */ + +template +class scoped_bilock:private noncopyable +{ +public: + scoped_bilock(Mutex& mutex1,Mutex& mutex2):mutex_eq(&mutex1==&mutex2) + { + bool mutex_lt=std::less()(&mutex1,&mutex2); + + ::new (static_cast(&lock1)) scoped_lock(mutex_lt?mutex1:mutex2); + if(!mutex_eq) + ::new (static_cast(&lock2)) scoped_lock(mutex_lt?mutex2:mutex1); + } + + ~scoped_bilock() + { + reinterpret_cast(&lock1)->~scoped_lock(); + if(!mutex_eq) + reinterpret_cast(&lock2)->~scoped_lock(); + } + +private: + typedef typename Mutex::scoped_lock scoped_lock; + typedef typename aligned_storage< + sizeof(scoped_lock), + alignment_of::value + >::type scoped_lock_space; + + bool mutex_eq; + scoped_lock_space lock1,lock2; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/hashed_index.hpp b/include/boost/multi_index/hashed_index.hpp index 1cbcd21..9c05c40 100644 --- a/include/boost/multi_index/hashed_index.hpp +++ b/include/boost/multi_index/hashed_index.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -88,12 +89,6 @@ template< > class hashed_index: BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - ,public safe_mode::safe_container< - hashed_index > -#endif - { #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ BOOST_WORKAROUND(__MWERKS__,<=0x3003) @@ -105,6 +100,12 @@ class hashed_index: #pragma parse_mfunc_templ off #endif +#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) + /* cross-index access */ + + template friend class index_base; +#endif + typedef typename SuperMeta::type super; protected: @@ -148,8 +149,7 @@ public: hashed_index_iterator< index_node_type,bucket_array_type, Category, - hashed_index_global_iterator_tag>, - hashed_index> iterator; + hashed_index_global_iterator_tag> > iterator; #else typedef hashed_index_iterator< index_node_type,bucket_array_type, @@ -191,19 +191,16 @@ protected: private: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - typedef safe_mode::safe_container< - hashed_index> safe_super; + typedef safe_mode::safe_container safe_container; #endif typedef typename call_traits::param_type value_param_type; 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: @@ -273,7 +270,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) @@ -508,6 +505,73 @@ public: this->final_swap_(x.final()); } + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(hashed_index,Index,void) + merge(Index& 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_CHECK_EQUAL_ALLOCATORS(*this,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_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,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;} @@ -687,6 +751,11 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: eq_(tuples::get<3>(args_list.get_head())), buckets(al,header()->impl(),tuples::get<0>(args_list.get_head())), mlf(1.0f) + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + ,safe(*this) +#endif + { calculate_max_load(); } @@ -694,17 +763,17 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: hashed_index( const hashed_index& x): super(x), - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super(), -#endif - key(x.key), hash_(x.hash_), eq_(x.eq_), buckets(x.get_allocator(),header()->impl(),x.buckets.size()), mlf(x.mlf), max_load(x.max_load) + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + ,safe(*this) +#endif + { /* Copy ctor just takes the internal configuration objects from x. The rest * is done in subsequent call to copy_(). @@ -715,16 +784,16 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: const hashed_index& x, do_not_copy_elements_tag): super(x,do_not_copy_elements_tag()), - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super(), -#endif - key(x.key), hash_(x.hash_), eq_(x.eq_), buckets(x.get_allocator(),header()->impl(),0), mlf(1.0f) + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + ,safe(*this) +#endif + { calculate_max_load(); } @@ -737,12 +806,12 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) iterator make_iterator(index_node_type* node) { - return iterator(node,this); + return iterator(node,&safe); } const_iterator make_iterator(index_node_type* node)const { - return const_iterator(node,const_cast(this)); + return const_iterator(node,const_cast(&safe)); } #else iterator make_iterator(index_node_type* node) @@ -894,13 +963,14 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: return res; } - void extract_(index_node_type* x) + template + void extract_(index_node_type* x,Dst dst) { unlink(x); - super::extract_(x); + super::extract_(x,dst.next()); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - detach_iterators(x); + transfer_iterators(dst.get(),x); #endif } @@ -944,7 +1014,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: buckets.clear(header()->impl()); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::detach_dereferenceable_iterators(); + safe.detach_dereferenceable_iterators(); #endif } @@ -961,7 +1031,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: std::swap(max_load,x.max_load); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::swap(x); + safe.swap(x.safe); #endif super::swap_(x,swap_allocators); @@ -975,7 +1045,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: std::swap(max_load,x.max_load); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::swap(x); + safe.swap(x.safe); #endif super::swap_elements_(x); @@ -1017,7 +1087,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: b=in_place(x->impl(),key(x->value()),buc); } BOOST_CATCH(...){ - extract_(x); + extract_(x,invalidate_iterators()); BOOST_RETHROW; } BOOST_CATCH_END @@ -1026,7 +1096,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: BOOST_TRY{ link_info pos(buckets.at(buc)); if(!link_point(x->value(),pos)){ - super::extract_(x); + super::extract_(x,invalidate_iterators()); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) detach_iterators(x); @@ -1036,7 +1106,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: link(x,pos); } BOOST_CATCH(...){ - super::extract_(x); + super::extract_(x,invalidate_iterators()); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) detach_iterators(x); @@ -1576,6 +1646,13 @@ private: iterator it=make_iterator(x); safe_mode::detach_equivalent_iterators(it); } + + template + void transfer_iterators(Dst& dst,index_node_type* x) + { + iterator it=make_iterator(x); + safe_mode::transfer_equivalent_iterators(dst,it); + } #endif template @@ -1696,7 +1773,11 @@ private: bucket_array_type buckets; float mlf; size_type max_load; - + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + safe_container safe; +#endif + #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ BOOST_WORKAROUND(__MWERKS__,<=0x3003) #pragma parse_mfunc_templ reset diff --git a/include/boost/multi_index/random_access_index.hpp b/include/boost/multi_index/random_access_index.hpp index 6ca05bc..183848f 100644 --- a/include/boost/multi_index/random_access_index.hpp +++ b/include/boost/multi_index/random_access_index.hpp @@ -1,4 +1,4 @@ -/* Copyright 2003-2020 Joaquin M Lopez Munoz. +/* 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) @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -80,12 +81,6 @@ namespace detail{ template class random_access_index: BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - ,public safe_mode::safe_container< - random_access_index > -#endif - { #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ BOOST_WORKAROUND(__MWERKS__,<=0x3003) @@ -97,6 +92,12 @@ class random_access_index: #pragma parse_mfunc_templ off #endif +#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) + /* cross-index access */ + + template friend class index_base; +#endif + typedef typename SuperMeta::type super; protected: @@ -120,8 +121,7 @@ public: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) typedef safe_mode::safe_iterator< - rnd_node_iterator, - random_access_index> iterator; + rnd_node_iterator > iterator; #else typedef rnd_node_iterator iterator; #endif @@ -168,18 +168,15 @@ protected: private: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - typedef safe_mode::safe_container< - random_access_index> safe_super; + typedef safe_mode::safe_container safe_container; #endif typedef typename call_traits< value_type>::param_type value_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: @@ -325,7 +322,7 @@ public: /* modifiers */ BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - emplace_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);} @@ -334,7 +331,7 @@ public: void pop_front(){erase(begin());} BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - emplace_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);} @@ -343,7 +340,7 @@ public: void pop_back(){erase(--end());} BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( - emplace_return_type,emplace,emplace_impl,iterator,position) + pair_return_type,emplace,emplace_impl,iterator,position) std::pair insert(iterator position,const value_type& x) { @@ -525,33 +522,36 @@ public: /* list operations */ - void splice(iterator position,random_access_index& x) + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(random_access_index,Index,void) + splice(iterator position,Index& x) { BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); - BOOST_MULTI_INDEX_CHECK_DIFFERENT_CONTAINER(*this,x); BOOST_MULTI_INDEX_RND_INDEX_CHECK_INVARIANT; - iterator first=x.begin(),last=x.end(); - size_type n=0; - BOOST_TRY{ - while(first!=last){ - if(push_back(*first).second){ - first=x.erase(first); - ++n; - } - else ++first; - } + if(x.end().get_node()==this->header()){ /* same container */ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( + position==end(),safe_mode::inside_range); } - BOOST_CATCH(...){ - relocate(position,end()-n,end()); - BOOST_RETHROW; + else{ + external_splice( + position,x,x.begin(),x.end(), + boost::is_copy_constructible()); } - BOOST_CATCH_END - relocate(position,end()-n,end()); } - void splice( - iterator position,random_access_index& x,iterator i) + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(random_access_index,Index,void) + splice(iterator position,BOOST_RV_REF(Index) x) + { + splice(position,static_cast(x)); + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE( + random_access_index,Index,pair_return_type) + splice( + iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i) { BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); @@ -559,28 +559,36 @@ public: BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i); BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x); BOOST_MULTI_INDEX_RND_INDEX_CHECK_INVARIANT; - if(&x==this)relocate(position,i); + if(x.end().get_node()==this->header()){ /* same container */ + index_node_type* pn=position.get_node(); + index_node_type* in=static_cast(i.get_node()); + if(pn!=in)relocate(pn,in); + return std::pair(make_iterator(in),true); + } else{ - if(insert(position,*i).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 - - } + std::pair p= + external_splice( + position,x,i,boost::is_copy_constructible()); + return std::pair(make_iterator(p.first),p.second); } } - void splice( - iterator position,random_access_index& x, - iterator first,iterator last) + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE( + random_access_index,Index,pair_return_type) + splice( + iterator position,BOOST_RV_REF(Index) x, + BOOST_DEDUCED_TYPENAME Index::iterator i) + { + return splice(position,static_cast(x),i); + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(random_access_index,Index,void) + splice( + iterator position,Index& x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last) { BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); @@ -590,25 +598,24 @@ public: BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x); BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last); BOOST_MULTI_INDEX_RND_INDEX_CHECK_INVARIANT; - if(&x==this)relocate(position,first,last); - else{ - size_type n=0; - BOOST_TRY{ - while(first!=last){ - if(push_back(*first).second){ - first=x.erase(first); - ++n; - } - else ++first; - } - } - BOOST_CATCH(...){ - relocate(position,end()-n,end()); - BOOST_RETHROW; - } - BOOST_CATCH_END - relocate(position,end()-n,end()); + if(x.end().get_node()==this->header()){ /* same container */ + BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(position,first,last); + internal_splice(position,first,last); } + else{ + external_splice( + position,x,first,last,boost::is_copy_constructible()); + } + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(random_access_index,Index,void) + splice( + iterator position,BOOST_RV_REF(Index) x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last) + { + splice(position,static_cast(x),first,last); } void remove(value_param_type value) @@ -745,17 +752,22 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: const ctor_args_list& args_list,const allocator_type& al): super(args_list.get_tail(),al), ptrs(al,header()->impl(),0) + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + ,safe(*this) +#endif + { } random_access_index(const random_access_index& x): super(x), + ptrs(x.get_allocator(),header()->impl(),x.size()) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super(), + ,safe(*this) #endif - ptrs(x.get_allocator(),header()->impl(),x.size()) { /* The actual copying takes place in subsequent call to copy_(). */ @@ -764,12 +776,12 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: random_access_index( const random_access_index& x,do_not_copy_elements_tag): super(x,do_not_copy_elements_tag()), + ptrs(x.get_allocator(),header()->impl(),0) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super(), + ,safe(*this) #endif - ptrs(x.get_allocator(),header()->impl(),0) { } @@ -780,9 +792,9 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) iterator make_iterator(index_node_type* node) - {return iterator(node,this);} + {return iterator(node,&safe);} const_iterator make_iterator(index_node_type* node)const - {return const_iterator(node,const_cast(this));} + {return const_iterator(node,const_cast(&safe));} #else iterator make_iterator(index_node_type* node){return iterator(node);} const_iterator make_iterator(index_node_type* node)const @@ -828,13 +840,14 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: return res; } - void extract_(index_node_type* x) + template + void extract_(index_node_type* x,Dst dst) { ptrs.erase(x->impl()); - super::extract_(x); + super::extract_(x,dst.next()); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - detach_iterators(x); + transfer_iterators(dst.get(),x); #endif } @@ -852,7 +865,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: ptrs.clear(); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::detach_dereferenceable_iterators(); + safe.detach_dereferenceable_iterators(); #endif } @@ -863,7 +876,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: ptrs.swap(x.ptrs,swap_allocators); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::swap(x); + safe.swap(x.safe); #endif super::swap_(x,swap_allocators); @@ -874,7 +887,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: ptrs.swap(x.ptrs); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::swap(x); + safe.swap(x.safe); #endif super::swap_elements_(x); @@ -1000,6 +1013,13 @@ private: iterator it=make_iterator(x); safe_mode::detach_equivalent_iterators(it); } + + template + void transfer_iterators(Dst& dst,index_node_type* x) + { + iterator it=make_iterator(x); + safe_mode::transfer_equivalent_iterators(dst,it); + } #endif template @@ -1085,7 +1105,121 @@ private: return std::pair(make_iterator(p.first),p.second); } - ptr_array ptrs; + template + std::pair external_splice( + iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i, + boost::true_type /* copy-constructible value */) + { + if(get_allocator()==x.get_allocator()){ + return external_splice(position,x,i,boost::false_type()); + } + else{ + /* backwards compatibility with old, non-transfer-based splice */ + + std::pair p=insert(position,*i); + if(p.second)x.erase(i); + return std::pair( + static_cast(p.first.get_node()),p.second); + } + } + + template + std::pair external_splice( + iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i, + boost::false_type /* copy-constructible value */) + { + BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x); + std::pair p=this->final_transfer_( + x,static_cast(i.get_node())); + if(p.second&&position.get_node()!=header()){ + relocate(position.get_node(),p.first); + } + return p; + } + + template + void internal_splice(iterator position,Iterator first,Iterator last) + { + /* null out [first, last) positions in ptrs array */ + + for(Iterator it=first;it!=last;++it){ + *(static_cast(it.get_node())->up())=0; + } + + node_impl_ptr_pointer pp=node_impl_type::gather_nulls( + ptrs.begin(),ptrs.end(), + static_cast(position.get_node())->up()); + + /* relink [first, last) */ + + for(Iterator it=first;it!=last;++it,++pp){ + *pp=static_cast(it.get_node()); + (*pp)->up()=pp; + } + } + + void internal_splice(iterator position,iterator first,iterator last) + { + index_node_type* pn=position.get_node(); + index_node_type* fn=static_cast(first.get_node()); + index_node_type* ln=static_cast(last.get_node()); + if(pn!=ln)relocate(pn,fn,ln); + } + + template + void external_splice( + iterator position,Index& x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last, + boost::true_type /* copy-constructible value */) + { + if(get_allocator()==x.get_allocator()){ + external_splice(position,x,first,last,boost::false_type()); + } + else{ + /* backwards compatibility with old, non-transfer-based splice */ + + size_type n=size(); + BOOST_TRY{ + while(first!=last){ + if(push_back(*first).second)first=x.erase(first); + else ++first; + } + } + BOOST_CATCH(...){ + relocate(position,begin()+n,end()); + BOOST_RETHROW; + } + BOOST_CATCH_END + relocate(position,begin()+n,end()); + } + } + + template + void external_splice( + iterator position,Index& x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last, + boost::false_type /* copy-constructible value */) + { + BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x); + size_type n=size(); + BOOST_TRY{ + this->final_transfer_range_(x,first,last); + } + BOOST_CATCH(...){ + relocate(position,begin()+n,end()); + BOOST_RETHROW; + } + BOOST_CATCH_END + relocate(position,begin()+n,end()); + } + + ptr_array ptrs; + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + safe_container safe; +#endif #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ BOOST_WORKAROUND(__MWERKS__,<=0x3003) diff --git a/include/boost/multi_index/sequenced_index.hpp b/include/boost/multi_index/sequenced_index.hpp index c1bdff5..fb09c60 100644 --- a/include/boost/multi_index/sequenced_index.hpp +++ b/include/boost/multi_index/sequenced_index.hpp @@ -1,4 +1,4 @@ -/* Copyright 2003-2020 Joaquin M Lopez Munoz. +/* 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) @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -70,12 +71,6 @@ namespace detail{ template class sequenced_index: BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - ,public safe_mode::safe_container< - sequenced_index > -#endif - { #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ BOOST_WORKAROUND(__MWERKS__,<=0x3003) @@ -87,6 +82,12 @@ class sequenced_index: #pragma parse_mfunc_templ off #endif +#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) + /* cross-index access */ + + template friend class index_base; +#endif + typedef typename SuperMeta::type super; protected: @@ -107,8 +108,7 @@ public: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) typedef safe_mode::safe_iterator< - bidir_node_iterator, - sequenced_index> iterator; + bidir_node_iterator > iterator; #else typedef bidir_node_iterator iterator; #endif @@ -155,17 +155,14 @@ protected: private: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - typedef safe_mode::safe_container< - sequenced_index> safe_super; + typedef safe_mode::safe_container safe_container; #endif typedef typename call_traits::param_type value_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: @@ -287,7 +284,7 @@ public: /* modifiers */ BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - emplace_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);} @@ -296,7 +293,7 @@ public: void pop_front(){erase(begin());} BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - emplace_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);} @@ -305,7 +302,7 @@ public: void pop_back(){erase(--end());} BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( - emplace_return_type,emplace,emplace_impl,iterator,position) + pair_return_type,emplace,emplace_impl,iterator,position) std::pair insert(iterator position,const value_type& x) { @@ -476,20 +473,36 @@ public: /* list operations */ - void splice(iterator position,sequenced_index& x) + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(sequenced_index,Index,void) + splice(iterator position,Index& x) { BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); - BOOST_MULTI_INDEX_CHECK_DIFFERENT_CONTAINER(*this,x); BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; - iterator first=x.begin(),last=x.end(); - while(first!=last){ - if(insert(position,*first).second)first=x.erase(first); - else ++first; + if(x.end().get_node()==this->header()){ /* same container */ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( + position==end(),safe_mode::inside_range); + } + else{ + external_splice( + position,x,x.begin(),x.end(), + boost::is_copy_constructible()); } } - void splice(iterator position,sequenced_index& x,iterator i) + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(sequenced_index,Index,void) + splice(iterator position,BOOST_RV_REF(Index) x) + { + splice(position,static_cast(x)); + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE( + sequenced_index,Index,pair_return_type) + splice( + iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i) { BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); @@ -497,30 +510,36 @@ public: BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i); BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x); BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; - if(&x==this){ - if(position!=i)relink(position.get_node(),i.get_node()); + if(x.end().get_node()==this->header()){ /* same container */ + index_node_type* pn=position.get_node(); + index_node_type* in=static_cast(i.get_node()); + if(pn!=in)relink(pn,in); + return std::pair(make_iterator(in),true); } else{ - if(insert(position,*i).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 - - } + std::pair p= + external_splice( + position,x,i,boost::is_copy_constructible()); + return std::pair(make_iterator(p.first),p.second); } } - void splice( - iterator position,sequenced_index& x, - iterator first,iterator last) + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE( + sequenced_index,Index,pair_return_type) + splice( + iterator position,BOOST_RV_REF(Index) x, + BOOST_DEDUCED_TYPENAME Index::iterator i) + { + return splice(position,static_cast(x),i); + } + + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(sequenced_index,Index,void) + splice( + iterator position,Index& x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last) { BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); @@ -530,19 +549,26 @@ public: BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x); BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last); BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; - if(&x==this){ + if(x.end().get_node()==this->header()){ /* same container */ BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(position,first,last); - if(position!=last)relink( - position.get_node(),first.get_node(),last.get_node()); + internal_splice(position,first,last); } else{ - while(first!=last){ - if(insert(position,*first).second)first=x.erase(first); - else ++first; - } + external_splice( + position,x,first,last,boost::is_copy_constructible()); } } + template + BOOST_MULTI_INDEX_ENABLE_IF_MERGEABLE(sequenced_index,Index,void) + splice( + iterator position,BOOST_RV_REF(Index) x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last) + { + splice(position,static_cast(x),first,last); + } + void remove(value_param_type value) { sequenced_index_remove( @@ -640,6 +666,11 @@ public: BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: sequenced_index(const ctor_args_list& args_list,const allocator_type& al): super(args_list.get_tail(),al) + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + ,safe(*this) +#endif + { empty_initialize(); } @@ -648,7 +679,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: super(x) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - ,safe_super() + ,safe(*this) #endif { @@ -660,7 +691,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: super(x,do_not_copy_elements_tag()) #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - ,safe_super() + ,safe(*this) #endif { @@ -674,9 +705,9 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) iterator make_iterator(index_node_type* node) - {return iterator(node,this);} + {return iterator(node,&safe);} const_iterator make_iterator(index_node_type* node)const - {return const_iterator(node,const_cast(this));} + {return const_iterator(node,const_cast(&safe));} #else iterator make_iterator(index_node_type* node){return iterator(node);} const_iterator make_iterator(index_node_type* node)const @@ -720,13 +751,14 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: return res; } - void extract_(index_node_type* x) + template + void extract_(index_node_type* x,Dst dst) { unlink(x); - super::extract_(x); + super::extract_(x,dst.next()); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - detach_iterators(x); + transfer_iterators(dst.get(),x); #endif } @@ -746,7 +778,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: empty_initialize(); #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::detach_dereferenceable_iterators(); + safe.detach_dereferenceable_iterators(); #endif } @@ -755,7 +787,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: sequenced_index& x,BoolConstant swap_allocators) { #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::swap(x); + safe.swap(x.safe); #endif super::swap_(x,swap_allocators); @@ -764,7 +796,7 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: void swap_elements_(sequenced_index& x) { #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - safe_super::swap(x); + safe.swap(x.safe); #endif super::swap_elements_(x); @@ -909,6 +941,13 @@ private: iterator it=make_iterator(x); safe_mode::detach_equivalent_iterators(it); } + + template + void transfer_iterators(Dst& dst,index_node_type* x) + { + iterator it=make_iterator(x); + safe_mode::transfer_equivalent_iterators(dst,it); + } #endif template @@ -978,6 +1017,106 @@ private: return std::pair(make_iterator(p.first),p.second); } + template + std::pair external_splice( + iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i, + boost::true_type /* copy-constructible value */) + { + if(get_allocator()==x.get_allocator()){ + return external_splice(position,x,i,boost::false_type()); + } + else{ + /* backwards compatibility with old, non-transfer-based splice */ + + std::pair p=insert(position,*i); + if(p.second)x.erase(i); + return std::pair( + static_cast(p.first.get_node()),p.second); + } + } + + template + std::pair external_splice( + iterator position,Index& x,BOOST_DEDUCED_TYPENAME Index::iterator i, + boost::false_type /* copy-constructible value */) + { + BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x); + std::pair p=this->final_transfer_( + x,static_cast(i.get_node())); + if(p.second&&position.get_node()!=header()){ + relink(position.get_node(),p.first); + } + return p; + } + + template + void internal_splice(iterator position,Iterator first,Iterator last) + { + index_node_type* pn=position.get_node(); + while(first!=last){ + relink(pn,static_cast((first++).get_node())); + } + } + + void internal_splice(iterator position,iterator first,iterator last) + { + index_node_type* pn=position.get_node(); + index_node_type* fn=static_cast(first.get_node()); + index_node_type* ln=static_cast(last.get_node()); + if(pn!=ln)relink(pn,fn,ln); + } + + template + void external_splice( + iterator position,Index& x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last, + boost::true_type /* copy-constructible value */) + { + if(get_allocator()==x.get_allocator()){ + external_splice(position,x,first,last,boost::false_type()); + } + else{ + /* backwards compatibility with old, non-transfer-based splice */ + + while(first!=last){ + if(insert(position,*first).second)first=x.erase(first); + else ++first; + } + } + } + + template + void external_splice( + iterator position,Index& x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last, + boost::false_type /* copy-constructible value */) + { + BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(*this,x); + if(position==end()){ + this->final_transfer_range_(x,first,last); + } + else{ + iterator first_to_relink=end(); + --first_to_relink; + BOOST_TRY{ + this->final_transfer_range_(x,first,last); + } + BOOST_CATCH(...){ + ++first_to_relink; + relink(position.get_node(),first_to_relink.get_node(),header()); + } + BOOST_CATCH_END + ++first_to_relink; + relink(position.get_node(),first_to_relink.get_node(),header()); + } + } + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + safe_container safe; +#endif + #if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ BOOST_WORKAROUND(__MWERKS__,<=0x3003) #pragma parse_mfunc_templ reset diff --git a/include/boost/multi_index_container.hpp b/include/boost/multi_index_container.hpp index 3d7e09b..9840a65 100644 --- a/include/boost/multi_index_container.hpp +++ b/include/boost/multi_index_container.hpp @@ -1,6 +1,6 @@ /* Multiply indexed container. * - * Copyright 2003-2020 Joaquin M Lopez Munoz. + * 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) @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -489,9 +490,7 @@ public: #endif BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); - BOOST_MULTI_INDEX_CHECK_IS_OWNER( - it,static_cast(*this)); - + BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,*this); return index_type::make_iterator( static_cast(it.get_node())); } @@ -508,8 +507,7 @@ public: #endif BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); - BOOST_MULTI_INDEX_CHECK_IS_OWNER( - it,static_cast(*this)); + BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,*this); return index_type::make_iterator( static_cast(it.get_node())); } @@ -541,8 +539,7 @@ public: #endif BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); - BOOST_MULTI_INDEX_CHECK_IS_OWNER( - it,static_cast(*this)); + BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,*this); return index_type::make_iterator( static_cast(it.get_node())); } @@ -559,8 +556,7 @@ public: #endif BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); - BOOST_MULTI_INDEX_CHECK_IS_OWNER( - it,static_cast(*this)); + BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,*this); return index_type::make_iterator( static_cast(it.get_node())); } @@ -767,6 +763,19 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: } } + template + std::pair transfer_(Index& x,final_node_type* n) + { + final_node_type* res=super::insert_(n->value(),n,&super::final(x)); + if(res==n){ + ++node_count; + return std::pair(res,true); + } + else{ + return std::pair(res,false); + } + } + template std::pair emplace_( BOOST_MULTI_INDEX_FUNCTION_PARAM_PACK) @@ -923,14 +932,21 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: final_node_handle_type extract_(final_node_type* x) { --node_count; - super::extract_(x); + super::extract_(x,detail::invalidate_iterators()); return final_node_handle_type(x,get_allocator()); } + template + void extract_for_transfer_(final_node_type* x,Dst dst) + { + --node_count; + super::extract_(x,dst); + } + void erase_(final_node_type* x) { --node_count; - super::extract_(x); + super::extract_(x,detail::invalidate_iterators()); delete_node_(x); } @@ -952,6 +968,17 @@ BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: node_count=0; } + template + void transfer_range_( + Index& x, + BOOST_DEDUCED_TYPENAME Index::iterator first, + BOOST_DEDUCED_TYPENAME Index::iterator last) + { + while(first!=last){ + transfer_(x,static_cast((first++).get_node())); + } + } + void swap_(multi_index_container& x) { swap_( @@ -1332,14 +1359,7 @@ project( #endif BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - typedef detail::converter< - multi_index_type, - BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter; - BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m)); -#endif - + BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,m); return detail::converter::iterator( m,static_cast(it.get_node())); } @@ -1368,14 +1388,7 @@ project( #endif BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - typedef detail::converter< - multi_index_type, - BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter; - BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m)); -#endif - + BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,m); return detail::converter::const_iterator( m,static_cast(it.get_node())); } @@ -1418,14 +1431,7 @@ project( #endif BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - typedef detail::converter< - multi_index_type, - BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter; - BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m)); -#endif - + BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,m); return detail::converter::iterator( m,static_cast(it.get_node())); } @@ -1455,14 +1461,7 @@ project( #endif BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - typedef detail::converter< - multi_index_type, - BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter; - BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m)); -#endif - + BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,m); return detail::converter::const_iterator( m,static_cast(it.get_node())); } diff --git a/test/test_list_ops.cpp b/test/test_list_ops.cpp index e67fda7..c736e97 100644 --- a/test/test_list_ops.cpp +++ b/test/test_list_ops.cpp @@ -1,6 +1,6 @@ /* Boost.MultiIndex test for standard list operations. * - * Copyright 2003-2013 Joaquin M Lopez Munoz. + * 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) @@ -76,6 +76,7 @@ template static void test_list_ops_unique_seq() { typedef typename nth_index::type sequenced_index; + typedef typename sequenced_index::iterator sequenced_index_iterator; Sequence ss,ss2; sequenced_index &si=get<1>(ss),&si2=get<1>(ss2); @@ -99,20 +100,32 @@ static void test_list_ops_unique_seq() CHECK_EQUAL(si,(3)(5)(1)); - si.splice(si.end(),si2); + si.splice(si.end(),si2,project<1>(ss2,ss2.find(2)),si2.end()); + CHECK_EQUAL(si,(3)(5)(1)(2)(6)); + CHECK_EQUAL(si2,(3)(4)(0)(8)(5)(1)); + + si.splice(project<1>(ss,ss.find(2)),si2); CHECK_EQUAL(si,(3)(5)(1)(4)(0)(8)(2)(6)); CHECK_EQUAL(si2,(3)(5)(1)); si.splice(project<1>(ss,ss.find(4)),si,project<1>(ss,ss.find(8))); CHECK_EQUAL(si,(3)(5)(1)(8)(4)(0)(2)(6)); - si2.clear(); - si2.splice(si2.begin(),si,si.begin()); - si.splice(si.end(),si2,si2.begin()); + si2.clear(); + std::pair p= + si2.splice(si2.begin(),si,si.begin()); + BOOST_TEST(*(p.first)==3&&p.second); + + p=si.splice(si.end(),si2,si2.begin()); + BOOST_TEST(*(p.first)==3&&p.second); CHECK_EQUAL(si,(5)(1)(8)(4)(0)(2)(6)(3)); BOOST_TEST(si2.empty()); - si2.splice(si2.end(),si,project<1>(ss,ss.find(0)),project<1>(ss,ss.find(6))); + si2.splice(si2.end(),si,project<1>(ss,ss.find(2)),project<1>(ss,ss.find(6))); + CHECK_EQUAL(si,(5)(1)(8)(4)(0)(6)(3)); + CHECK_EQUAL(si2,(2)); + + si2.splice(si2.begin(),si,project<1>(ss,ss.find(0)),project<1>(ss,ss.find(6))); CHECK_EQUAL(si,(5)(1)(8)(4)(6)(3)); CHECK_EQUAL(si2,(0)(2)); diff --git a/test/test_node_handling.cpp b/test/test_node_handling.cpp index 5f8e169..6d9002d 100644 --- a/test/test_node_handling.cpp +++ b/test/test_node_handling.cpp @@ -1,6 +1,6 @@ /* Boost.MultiIndex test for node handling operations. * - * Copyright 2003-2020 Joaquin M Lopez Munoz. + * 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) @@ -23,8 +23,12 @@ #include #include #include -#include +#include +#include #include +#include +#include +#include #include "count_allocator.hpp" using namespace boost::multi_index; @@ -136,7 +140,8 @@ template struct is_key_based:boost::integral_constant< bool, /* rather fragile if new index types are included in the library */ - (boost::tuples::length::value > 0) + (boost::tuples::length:: + type::ctor_args>::value > 0) > {}; @@ -403,8 +408,410 @@ void test_transfer() 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(); } diff --git a/test/test_safe_mode.cpp b/test/test_safe_mode.cpp index 92fae87..ff18012 100644 --- a/test/test_safe_mode.cpp +++ b/test/test_safe_mode.cpp @@ -1,6 +1,6 @@ /* Boost.MultiIndex test for safe_mode. * - * Copyright 2003-2020 Joaquin M Lopez Munoz. + * 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) @@ -326,7 +326,7 @@ static void local_test_safe_mode_with_rearrange() TRY_SAFE_MODE i.splice(i.begin(),i); - CATCH_SAFE_MODE(safe_mode::same_container) + CATCH_SAFE_MODE(safe_mode::inside_range) TRY_SAFE_MODE iterator it;