From a52810fc3d246384ca8a74d0f9676bc58e216735 Mon Sep 17 00:00:00 2001
From: joaquintides
Date: Thu, 19 Aug 2021 10:41:03 +0200
Subject: [PATCH] implemented merge operations (#49)
* initial draft
* modified appveyor.yml for this branch
* dropped BOOST_RV_REF
as it doesn't handle lvalue refs
* befriended index_base from all indices
so as to be grant access to final_extract_for_merge_ from an external container
* added rvalue ref merge
(test expected to fail in C++03)
* added explicit boost::move on rvalue ref
for the benefit of C++03 compilers
* fixed previous workaround
* refactored merge algorithm into multi_index_container
* added hashed_index::merge
* reimplemented sequenced_index::splice(position,x)
* added missing this->'s
* refactored SFINAEing code
* made sequenced_index::splice(position,x) exception robust
* reimplemented random_access_index::splice(position,x)
* micro-optimized legacy code in random_access_index::splice(position,x)
* added missing #include
* reimplemented sequenced_index::splice(position,x,i)
* reimplemented random_access_index::splice(position,x,i)
* reimplemented sequenced_index::splice(position,x,first,last)
* reimplemented random_access_index::splice(position,x,first,last)
* re-engineered sequenced_index::splice(position,x,i)
so that it works when source and destination belong to the same container
* stylistic
* re-engineered random_access_index::splice(position,x,i)
so that it works when source and destination belong to the same container
* re-engineered sequenced_index::splice(position,x,first,last)
so that it works when source and destination belong to the same container
* stylistic
* fixed bug in rvalue ref overload of sequenced_index::splice(position,x,first,last)
* fixed internal sequenced_index::splice(position,x,first,last) across different indices
* re-engineered random_access_index::splice(position,x,first,last)
the same way as done with sequenced_index
* replaced multi_index_container::merge_ with transfer_range_
so as to refactor some code in non-key-based indices
* fixed safe mode check for different-type containers
* hardened merge/splice tests
* s/BOOST_RV_REF/BOOST_FWD_REF
* called the right merge_different overload
* fixed problem with Boost.Move C++03 perfect fwd emulation
* updated (C) year
* allowed intra-container full splice
* required position==end() in intra-container full splice
* made pointwise splice return a pair
* added partial merge functionality to key-based indices
plus fixed a compile-time error with legacy splice code when applied to different-type indices
* suppressed unused variable
* fixed wrong signature in splice memfun
* made safe-mode iterators SCARY
* refactored any_container_view
* implemented safe iterator transfer in merge/splice
* reorganized internal deps
* stylistic
* automatically checked iterator invalidation upon merge/splice
* removed commented-out code
* checked allocator equality
* reimplemented random_access_index internal, cross-index splice in linear time
* updated docs
* reverted appveyor.yml
---
appveyor.yml | 4 +-
doc/reference/hash_indices.html | 99 ++++-
doc/reference/ord_indices.html | 105 ++++-
doc/reference/rnd_indices.html | 195 ++++++---
doc/reference/rnk_indices.html | 12 +-
doc/reference/seq_indices.html | 199 ++++++---
doc/release_notes.html | 32 +-
doc/tutorial/basics.html | 71 +--
doc/tutorial/indices.html | 112 ++++-
.../multi_index/detail/any_container_view.hpp | 76 ++++
.../detail/index_access_sequence.hpp | 74 ++++
.../boost/multi_index/detail/index_base.hpp | 36 +-
.../detail/invalidate_iterators.hpp | 44 ++
.../boost/multi_index/detail/node_handle.hpp | 25 +-
.../multi_index/detail/ord_index_impl.hpp | 156 +++++--
.../multi_index/detail/rnd_index_node.hpp | 21 +-
.../boost/multi_index/detail/safe_mode.hpp | 275 ++++++++----
.../multi_index/detail/scoped_bilock.hpp | 70 +++
include/boost/multi_index/hashed_index.hpp | 155 +++++--
.../boost/multi_index/random_access_index.hpp | 306 +++++++++----
include/boost/multi_index/sequenced_index.hpp | 261 ++++++++---
include/boost/multi_index_container.hpp | 87 ++--
test/test_list_ops.cpp | 25 +-
test/test_node_handling.cpp | 413 +++++++++++++++++-
test/test_safe_mode.cpp | 4 +-
25 files changed, 2300 insertions(+), 557 deletions(-)
create mode 100644 include/boost/multi_index/detail/any_container_view.hpp
create mode 100644 include/boost/multi_index/detail/index_access_sequence.hpp
create mode 100644 include/boost/multi_index/detail/invalidate_iterators.hpp
create mode 100644 include/boost/multi_index/detail/scoped_bilock.hpp
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);
+
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).
@@ -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
+
+ - the index is non-unique OR no other element exists with
+ equivalent key,
+ - AND insertion is allowed by all other indices of the
+
multi_index_container
.
+
+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
+
+ - the index is non-unique OR no other element exists with
+ equivalent key,
+ - AND insertion is allowed by all other indices of the
+
multi_index_container
.
+
+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.
+
+
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
- Complexity signature
- Instantiation types
+ - Nested types
- Constructors, copy and assignment
- Iterators
- Modifiers
@@ -310,6 +311,16 @@ do not exactly conform to or are not mandated by the standard requirements.
void swap(index class name& x);
void clear()noexcept;
+ 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);
+
key_from_value key_extractor()const;
@@ -465,6 +476,15 @@ must be a model of
on elements of KeyFromValue::result_type
.
+
+
+iterator
+const_iterator
+
+
+These types depend only on node_type
.
+
+
@@ -911,6 +931,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
+
+ - the index is non-unique OR no other element exists with
+ equivalent key,
+ - AND insertion is allowed by all other indices of the
+
multi_index_container
.
+
+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
+
+ - the index is non-unique OR no other element exists with
+ equivalent key,
+ - AND insertion is allowed by all other indices of the
+
multi_index_container
.
+
+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.
+
+
Apart from standard key_comp
and value_comp
,
@@ -1237,7 +1340,7 @@ Ranked 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/rnd_indices.html b/doc/reference/rnd_indices.html
index dd6a799..f92a9ae 100644
--- a/doc/reference/rnd_indices.html
+++ b/doc/reference/rnd_indices.html
@@ -40,6 +40,7 @@ Key extraction
- Complexity signature
- Instantiation types
+ - Nested types
- Constructors, copy and assignment
- Iterators
- Capacity operations
@@ -316,10 +317,16 @@ plus the requirements for std::list
specific list operations at
- void splice(iterator position,index class name& x);
- void splice(iterator position,index class name& x,iterator i);
+ template<typename Index> void splice(const_iterator position,Index&& x);
+ template<typename Index>
+ std::pair<iterator,bool> splice(
+ const_iterator position,Index&& x,
+ typename std::remove_reference_t<Index>::const_iterator i);
+ template<typename Index>
void splice(
- iterator position,index class name& x,iterator first,iterator last);
+ const_iterator position,Index&& x,
+ typename std::remove_reference_t<Index>::const_iterator first,
+ typename std::remove_reference_t<Index>::const_iterator last);
void remove(const value_type& value);
template<typename Predicate> void remove_if(Predicate pred);
@@ -450,6 +457,15 @@ index specifier. Instantiations are dependent on the following types:
tag
.
+
+
+iterator
+const_iterator
+
+
+These types depend only on node_type
.
+
+
@@ -875,56 +891,116 @@ The syntax and behavior of these operations exactly matches those
of sequenced indices, but the associated complexity bounds differ in general.
-void splice(iterator position,index class name& x);
+ template<typename Index> void splice(const_iterator position,Index&& x);
-Requires: position
is a valid iterator of the index.
-&x!=this
.
-Effects: Inserts the contents of x
before position
,
-in the same order as they were in x
. Those elements successfully
-inserted are erased from x
.
-Complexity: O(shl(end()-position,x.size()) + x.size()*I(n+x.size()) + x.size()*D(x.size()))
.
-Exception safety: Basic.
+Requires: x
is a non-const reference to an index of a
+node-compatible
+multi_index_container
.
+position
is a valid iterator of the index, and must be exactly end()
+if the source and destination containers are the same.
+Effects:
+
+splice(position,x,x.begin(),x.end());
+
-void splice(iterator position,index class name& x,iterator i);
+
+template<typename Index> std::pair<iterator,bool> splice(
+ const_iterator position,Index&& x,
+ typename std::remove_reference_t<Index>::const_iterator i);
+
-Requires: position
is a valid iterator of the index.
-i
is a valid dereferenceable iterator x
.
-Effects: Inserts the element pointed to by i
before
-position
: if insertion is successful, the element is erased from
-x
. In the special case &x==this
, no copy or
-deletion is performed, and the operation is always successful. If
-position==i
, no operation is performed.
-Postconditions: If &x==this
, no iterator or reference
-is invalidated.
-Complexity: If &x==this
, O(rel(position,i,i+1))
;
-otherwise O(shl(end()-position,1) + I(n) + D(n))
.
-Exception safety: If &x==this
, nothrow
;
-otherwise, strong.
+Requires: x
is a non-const reference to an index of a
+node-compatible
+multi_index_container
.
+If get_allocator()!=x.get_allocator()
,
+value_type
must be CopyInsertable
into the destination
+multi_index_container
.
+position
is a valid iterator of the index.
+i
is a valid dereferenceable iterator of x
.
+Effects:
+
+ - (Same container) if the source and destination containers are the same, repositions
+ the element pointed to by
i
before position
unless both
+ iterators refer to the same element.
+ - (Transfer splice) else, if
get_allocator()==x.get_allocator()
,
+ transfers the node of the element referred to by i
into the
+ destination multi_index_container
right before position
+ if insertion is allowed by all other indices of the multi_index_container
.
+ - (Destructive splice) else, insertion of
*i
is tried before
+ position
; if the operation is successful, the element is erased from x
.
+
+
+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 insertion (either through transfer or copy insertion)
+took place or the source and destination containers are the same.
+If p.second
is true
,
+p.first
points to the inserted element or to *i
if the
+source and destination containers are the same; 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,
+O(rel(position,i',i'+1))
, where i'
is the projection
+of i
into the index of position
;
+otherwise, O(shl(end()-position,1) + I(n) + D(x.size()))
.
+Exception safety: If the source and destination containers are the same,
+nothrow
; otherwise strong.
+Implementation note: The destructive variant of this operation is provided
+for reasons of backwards compatibility with previous versions of this library where
+allocator equality was not required.
-void splice(iterator position,index class name& x,iterator first,iterator last);
+
+template<typename Index> void splice(
+ const_iterator position,Index&& x,
+ typename std::remove_reference_t<Index>::const_iterator first,
+ typename std::remove_reference_t<Index>::const_iterator last);
+
-Requires: position
is a valid iterator of the index.
-first
and last
are valid iterators of x
.
-last
is reachable from first
. position
-is not in the range [first
,last
).
-Effects: For each element in the range [first
,last
),
-insertion is tried before position
; if the operation is successful,
-the element is erased from x
. In the special case
-&x==this
, no copy or deletion is performed, and insertions are
-always successful.
-Postconditions: If &x==this
, no iterator or reference
-is invalidated.
-Complexity: If &x==this
,
-O(rel(position,first,last))
; otherwise
-O(shl(end()-position,m) + m*I(n+m) + m*D(x.size()))
-where m
is the number of elements in [first
,last
).
-Exception safety: If &x==this
, nothrow
;
-otherwise, basic.
+Requires: x
is a non-const reference to an index of a
+node-compatible
+multi_index_container
.
+If get_allocator()!=x.get_allocator()
,
+value_type
must be CopyInsertable
into the destination
+multi_index_container
.
+position
is a valid iterator of the index and does not point to any element in
+[first
,last
).
+[first
,last
) is a valid range of x
.
+Effects:
+
+ - (Same container) if the source and destination containers are the same, repositions all the elements
+ of [
first
,last
), in this order, before position
.
+
+ - (Transfer splice) else, if
get_allocator()==x.get_allocator()
, then, for each node in
+ [first
,last
), in this order, the node is transferred to the
+ multi_index_container
right before position
+ if insertion is allowed by all other indices of the
+ multi_index_container
.
+ - (Destructive splice) else, for each element in [
first
,last
), in this order,
+ insertion is tried before position
; if the operation is successful, the
+ element is erased from x
.
+
+
+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 &x==this
, O(rel(position,first,last))
;
+else, if the source and destination containers are the same, O(n)
;
+otherwise, O(shl(end()-position,m) + 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.
+Implementation note: The destructive variant of this operation is provided
+for reasons of backwards compatibility with previous versions of this library where
+allocator equality was not required.
void remove(const value_type& value);
@@ -973,20 +1049,22 @@ is the number of elements erased.
void merge(index class name& x);
-Requires: std::less<value_type>
induces a
+Requires:
+Either get_allocator()==x.get_allocator()
or value_type
+is CopyInsertable
into the multi_index_container
.
+std::less<value_type>
induces a
strict weak ordering over value_type
.
Both the index and x
are sorted according to
std::less<value_type>
.
-Effects: Attempts to insert every element of x
into the
-corresponding position of the index (according to the order). Elements
-successfully inserted are erased from x
. The resulting sequence
+Effects: Attempts to splice every element of x
into the
+corresponding position of the index (according to the order). The resulting sequence
is stable, i.e. equivalent elements of either container preserve their
relative position. In the special case &x==this
, no operation
is performed.
Postconditions: Elements in the index and remaining elements in
x
are sorted.
-Validity of iterators to the index and of non-erased elements of x
-references is preserved.
+Validity of iterators and references is preserved, except to elements removed
+from x
if get_allocator()!=x.get_allocator()
.
Complexity: If &x==this
, constant; otherwise
O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))
.
Exception safety: If &x==this
, nothrow
;
@@ -996,19 +1074,20 @@ otherwise, basic.
template <typename Compare> void merge(index class name& x,Compare comp);
-Requires: Compare
induces a
-strict weak ordering over value_type
.
+Requires:
+Either get_allocator()==x.get_allocator()
or value_type
+is CopyInsertable
into the multi_index_container
.
+Compare
induces a strict weak ordering over value_type
.
Both the index and x
are sorted according to comp
.
-Effects: Attempts to insert every element of x
into the
-corresponding position of the index (according to comp
).
-Elements successfully inserted are erased from x
. The resulting
+Effects: Attempts to splice every element of x
into the
+corresponding position of the index (according to comp
). The resulting
sequence is stable, i.e. equivalent elements of either container preserve
their relative position. In the special case &x==this
, no
operation is performed.
Postconditions: Elements in the index and remaining elements in
x
are sorted according to comp
.
-Validity of iterators to the index and of non-erased elements of x
-references is preserved.
+Validity of iterators and references is preserved, except to elements removed
+from x
if get_allocator()!=x.get_allocator()
.
Complexity: If &x==this
, constant; otherwise
O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))
.
Exception safety: If &x==this
, nothrow
;
@@ -1160,9 +1239,9 @@ Key extraction
-Revised May 9th 2020
+Revised August 16th 2021
-© Copyright 2003-2020 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
diff --git a/doc/reference/rnk_indices.html b/doc/reference/rnk_indices.html
index 9904b54..bfa3b44 100644
--- a/doc/reference/rnk_indices.html
+++ b/doc/reference/rnk_indices.html
@@ -42,6 +42,7 @@ Hashed indices
@@ -473,6 +474,15 @@ These types are subject to the same requirements as specified for
ordered indices.
+
+
+iterator
+const_iterator
+
+
+These types depend only on node_type
.
+
+
@@ -637,7 +647,7 @@ Hashed indices
-
Revised July 29th 2021
+Revised August 14th 2021
© Copyright 2003-2021 Joaquín M López Muñoz.
Distributed under the Boost Software
diff --git a/doc/reference/seq_indices.html b/doc/reference/seq_indices.html
index 385fa1b..80e0f14 100644
--- a/doc/reference/seq_indices.html
+++ b/doc/reference/seq_indices.html
@@ -40,6 +40,7 @@ Random access indices
- Complexity signature
- Instantiation types
+ - Nested types
- Constructors, copy and assignment
- Iterators
- Capacity operations
@@ -299,10 +300,16 @@ most important differences are:
- void splice(iterator position,index class name& x);
- void splice(iterator position,index class name& x,iterator i);
+ template<typename Index> void splice(const_iterator position,Index&& x);
+ template<typename Index>
+ std::pair<iterator,bool> splice(
+ const_iterator position,Index&& x,
+ typename std::remove_reference_t<Index>::const_iterator i);
+ template<typename Index>
void splice(
- iterator position,index class name& x,iterator first,iterator last);
+ const_iterator position,Index&& x,
+ typename std::remove_reference_t<Index>::const_iterator first,
+ typename std::remove_reference_t<Index>::const_iterator last);
void remove(const value_type& value);
template<typename Predicate> void remove_if(Predicate pred);
@@ -420,6 +427,15 @@ index specifier. Instantiations are dependent on the following types:
tag
.
+
+
+iterator
+const_iterator
+
+
+These types depend only on node_type
.
+
+
@@ -802,62 +818,122 @@ is rethrown.
Sequenced indices provide the full set of list operations found in
-std::list
; the semantics of these member functions, however,
-differ from that of std::list
in some cases as insertions
-might not succeed due to banning by other indices. Similarly, the complexity
+std::list
, extended in some cases such as splice
;
+the semantics of these member functions, however,
+typically differs from that of std::list
as insertions
+may not succeed due to banning by other indices. Similarly, the complexity
of the operations may depend on the other indices belonging to the
same multi_index_container
.
-void splice(iterator position,index class name& x);
+template<typename Index> void splice(const_iterator position,Index&& x);
-Requires: position
is a valid iterator of the index.
-&x!=this
.
-Effects: Inserts the contents of x
before position
,
-in the same order as they were in x
. Those elements successfully
-inserted are erased from x
.
-Complexity: O(x.size()*I(n+x.size()) + x.size()*D(x.size()))
.
-Exception safety: Basic.
+Requires: x
is a non-const reference to an index of a
+node-compatible
+multi_index_container
.
+position
is a valid iterator of the index, and must be exactly end()
+if the source and destination containers are the same.
+Effects:
+
+splice(position,x,x.begin(),x.end());
+
-void splice(iterator position,index class name& x,iterator i);
+
+template<typename Index> std::pair<iterator,bool> splice(
+ const_iterator position,Index&& x,
+ typename std::remove_reference_t<Index>::const_iterator i);
+
-Requires: position
is a valid iterator of the index.
-i
is a valid dereferenceable iterator x
.
-Effects: Inserts the element pointed to by i
before
-position
: if insertion is successful, the element is erased from
-x
. In the special case &x==this
, no copy or
-deletion is performed, and the operation is always successful. If
-position==i
, no operation is performed.
-Postconditions: If &x==this
, no iterator or reference
-is invalidated.
-Complexity: If &x==this
, constant; otherwise
-O(I(n) + D(n))
.
-Exception safety: If &x==this
, nothrow
;
-otherwise, strong.
+Requires: x
is a non-const reference to an index of a
+node-compatible
+multi_index_container
.
+If get_allocator()!=x.get_allocator()
,
+value_type
must be CopyInsertable
into the destination
+multi_index_container
.
+position
is a valid iterator of the index.
+i
is a valid dereferenceable iterator of x
.
+Effects:
+
+ - (Same container) if the source and destination containers are the same, repositions
+ the element pointed to by
i
before position
unless both
+ iterators refer to the same element.
+ - (Transfer splice) else, if
get_allocator()==x.get_allocator()
,
+ transfers the node of the element referred to by i
into the
+ destination multi_index_container
right before position
+ if insertion is allowed by all other indices of the multi_index_container
.
+ - (Destructive splice) else, insertion of
*i
is tried before
+ position
; if the operation is successful, the element is erased from x
.
+
+
+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 insertion (either through transfer or copy insertion)
+took place or the source and destination containers are the same.
+If p.second
is true
,
+p.first
points to the inserted element or to *i
if the
+source and destination containers are the same; 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.
+Implementation note: The destructive variant of this operation is provided
+for reasons of backwards compatibility with previous versions of this library where
+allocator equality was not required.
-void splice(iterator position,index class name& x,iterator first,iterator last);
+
+template<typename Index> void splice(
+ const_iterator position,Index&& x,
+ typename std::remove_reference_t<Index>::const_iterator first,
+ typename std::remove_reference_t<Index>::const_iterator last);
+
-Requires: position
is a valid iterator of the index.
-first
and last
are valid iterators of x
.
-last
is reachable from first
. position
-is not in the range [first
,last
).
-Effects: For each element in the range [first
,last
),
-insertion is tried before position
; if the operation is successful,
-the element is erased from x
. In the special case
-&x==this
, no copy or deletion is performed, and insertions are
-always successful.
-Postconditions: If &x==this
, no iterator or reference
-is invalidated.
-Complexity: If &x==this
, constant; otherwise
-O(m*I(n+m) + m*D(x.size()))
where m
is the number
-of elements in [first
,last
).
-Exception safety: If &x==this
, nothrow
;
-otherwise, basic.
+Requires: x
is a non-const reference to an index of a
+node-compatible
+multi_index_container
.
+If get_allocator()!=x.get_allocator()
,
+value_type
must be CopyInsertable
into the destination
+multi_index_container
.
+position
is a valid iterator of the index and does not point to any element in
+[first
,last
).
+[first
,last
) is a valid range of x
.
+Effects:
+
+ - (Same container) if the source and destination containers are the same, repositions all the elements
+ of [
first
,last
), in this order, before position
.
+
+ - (Transfer splice) else, if
get_allocator()==x.get_allocator()
, then, for each node in
+ [first
,last
), in this order, the node is transferred to the
+ multi_index_container
right before position
+ if insertion is allowed by all other indices of the
+ multi_index_container
.
+ - (Destructive splice) else, for each element in [
first
,last
), in this order,
+ insertion is tried before position
; if the operation is successful, the
+ element is erased from x
.
+
+
+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 &x==this
, constant; else, if
+the source and destination containers are the same, O(m)
;
+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.
+Implementation note: The destructive variant of this operation is provided
+for reasons of backwards compatibility with previous versions of this library where
+allocator equality was not required.
void remove(const value_type& value);
@@ -906,20 +982,22 @@ is the number of elements erased.
void merge(index class name& x);
-Requires: std::less<value_type>
induces a
+Requires:
+Either get_allocator()==x.get_allocator()
or value_type
+is CopyInsertable
into the multi_index_container
.
+std::less<value_type>
induces a
strict weak ordering over value_type
.
Both the index and x
are sorted according to
std::less<value_type>
.
-Effects: Attempts to insert every element of x
into the
-corresponding position of the index (according to the order). Elements
-successfully inserted are erased from x
. The resulting sequence
+Effects: Attempts to splice every element of x
into the
+corresponding position of the index (according to the order). The resulting sequence
is stable, i.e. equivalent elements of either container preserve their
relative position. In the special case &x==this
, no operation
is performed.
Postconditions: Elements in the index and remaining elements in
x
are sorted.
-Validity of iterators to the index and of non-erased elements of x
-references is preserved.
+Validity of iterators and references is preserved, except to elements removed
+from x
if get_allocator()!=x.get_allocator()
.
Complexity: If &x==this
, constant; otherwise
O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))
.
Exception safety: If &x==this
, nothrow
;
@@ -929,19 +1007,20 @@ otherwise, basic.
template <typename Compare> void merge(index class name& x,Compare comp);
-Requires: Compare
induces a
-strict weak ordering over value_type
.
+Requires:
+Either get_allocator()==x.get_allocator()
or value_type
+is CopyInsertable
into the multi_index_container
.
+Compare
induces a strict weak ordering over value_type
.
Both the index and x
are sorted according to comp
.
-Effects: Attempts to insert every element of x
into the
-corresponding position of the index (according to comp
).
-Elements successfully inserted are erased from x
. The resulting
+Effects: Attempts to splice every element of x
into the
+corresponding position of the index (according to comp
). The resulting
sequence is stable, i.e. equivalent elements of either container preserve
their relative position. In the special case &x==this
, no
operation is performed.
Postconditions: Elements in the index and remaining elements in
x
are sorted according to comp
.
-Validity of iterators to the index and of non-erased elements of x
-references is preserved.
+Validity of iterators and references is preserved, except to elements removed
+from x
if get_allocator()!=x.get_allocator()
.
Complexity: If &x==this
, constant; otherwise
O(n + x.size()*I(n+x.size()) + x.size()*D(x.size()))
.
Exception safety: If &x==this
, nothrow
;
@@ -1095,9 +1174,9 @@ Random access indices
-Revised May 9th 2020
+Revised August 16th 2021
-© Copyright 2003-2020 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
diff --git a/doc/release_notes.html b/doc/release_notes.html
index 11046c2..ef7ec43 100644
--- a/doc/release_notes.html
+++ b/doc/release_notes.html
@@ -72,6 +72,34 @@ Acknowledgements
- Added
contains
to key-based indices
(issue #35).
+ - Added
merge
operations to key-based indices. The functionality goes beyond
+ the standard specification for (unordered) associative containers in a number of ways,
+ most notably:
+
+ - The source index can be of any type, including non key-based indices.
+ - Partial merge is provided: for instance,
x.merge(y,first,last)
+ merges only the elements of y
within [first
,last
).
+
+
+
+ - Previous versions of
splice
for sequenced and random access indices
+ were destructive, i.e. elements were copy-inserted into the destination and then erased
+ from the source. Now, splice
is based on node transfer much as merge
+ in key-based indices, and has been similarly extended to accept source indices of any type:
+ in fact, splice
can be regarded as a frontend to the same functionality
+ provided by merge
in key-based indices. For reasons of backwards compatibility,
+ the destructive behavior of splice
has been retained in the case that the
+ source and destination containers have unequal allocators.
+
+ - The fact has been documented that index iterator types do only depend on
node_type
,
+ (except for hashed indices, where uniqueness/non-uniqueness is also a dependency). This has
+ implications on the validity of iterators to elements transferred by merge
or
+ splice
. This property is a variant of what has been called
+ SCARY iterators in the C++ standard mailing lists.
+ SCARYness is currently (August 2021) not mandated for standard containers.
+
+ - Iterator SCARYness is now also preserved in safe mode.
+
@@ -88,7 +116,7 @@ Acknowledgements
-
- Added node extraction and insertion
+ Added node extraction and insertion
following the analogous interface of associative containers as introduced in C++17.
This feature has also been extended to non key-based indices, in contrast to C++
standard library sequence containers, which do not provide such functionality.
@@ -695,7 +723,7 @@ Acknowledgements
-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/tutorial/basics.html b/doc/tutorial/basics.html
index 8a5a6fa..8f14652 100644
--- a/doc/tutorial/basics.html
+++ b/doc/tutorial/basics.html
@@ -62,7 +62,6 @@ Index types
- Projection of iterators
- - Node handling operations
- Complexity and exception safety
@@ -1217,72 +1216,6 @@ When provided, project
can also be used with
tags.
-
-
-
-Using direct node manipulation, elements can be passed between
-multi_index_container
s without actually copying them:
-
-
-
-
-void move_to_retirement(int ssnumber,employee_set& es,employee_set& archive)
-{
-
- employee_set_by_ssn::node_type node=es.get<ssn>().extract(ssnumber);
-
- if(!node.empty()){
- 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;
-
-...
-
-
-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_container
s 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.
-
@@ -1317,9 +1250,9 @@ Index types
-
Revised May 9th 2020
+Revised August 16th 2021
-© Copyright 2003-2020 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
diff --git a/doc/tutorial/indices.html b/doc/tutorial/indices.html
index b1f13bc..dee316e 100644
--- a/doc/tutorial/indices.html
+++ b/doc/tutorial/indices.html
@@ -55,6 +55,7 @@ Key extraction
Index rearranging
iterator_to
+ Node handling operations
Ordered indices node compression
@@ -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_container
s, 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:
+
+
+
+Using direct node manipulation, elements can be passed between
+multi_index_container
s without actually copying them:
+
+
+
+
+void move_to_retirement(int ssnumber,employee_set& es,employee_set& archive)
+{
+
+
+ employee_set_by_ssn::node_type node=es.get<ssn>().extract(ssnumber);
+
+ if(!node.empty()){
+ 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;
+
+...
+
+
+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_container
s 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_type
s thanks to merge
(key-based
+indices) and splice
(non key-based indices).
+
+
+
+
+void move_to_retirement_by_age(
+ int max_age,employee_set& es,employee_set& archive)
+{
+
+ employee_set_by_age& ea=es.get<age>();
+
+
+ archive.merge(ea,ea.upper_bound(max_age),ea.end());
+}
+
+...
+
+
+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.
+
+
@@ -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