cpp-btree: Add support for uncopyable/move-only map/multimap value types

This commit is contained in:
Jonathan G Rennison
2023-05-26 20:49:14 +01:00
parent b381a30d2d
commit f00e0e195f
4 changed files with 139 additions and 71 deletions

View File

@@ -40,6 +40,9 @@ Changes include:
- Adding key_comp - Adding key_comp
- Adding move constructors/assignment. - Adding move constructors/assignment.
- No longer #define NDEBUG if unset, this can clash with other headers, test for BTREE_DEBUG being defined instead - No longer #define NDEBUG if unset, this can clash with other headers, test for BTREE_DEBUG being defined instead
- Add noexcept to swap-based move constructors and copy/move assignment operator
- Remove use of deprecated std::allocator::rebind type
- Adding support for uncopyable/move-only map/multimap values (not keys).
CMakeLists-pthreads-fix.txt is a modified copy of CMakeLists.txt which includes -lpthreads when building the tests/benchmarks. CMakeLists-pthreads-fix.txt is a modified copy of CMakeLists.txt which includes -lpthreads when building the tests/benchmarks.
Using this instead fixes compilation on some platforms. Using this instead fixes compilation on some platforms.

View File

@@ -649,9 +649,14 @@ class btree_node {
return s; return s;
} }
private:
void insert_value_common(int i);
public:
// Inserts the value x at position i, shifting all existing values and // Inserts the value x at position i, shifting all existing values and
// children at positions >= i to the right by 1. // children at positions >= i to the right by 1.
void insert_value(int i, const value_type &x); template <typename... Args>
void insert_value(int i, Args&&... args);
// Removes the value at position i, shifting all existing values and children // Removes the value at position i, shifting all existing values and children
// at positions > i to the left by 1. // at positions > i to the left by 1.
@@ -709,8 +714,9 @@ class btree_node {
void value_init(int i) { void value_init(int i) {
new (&fields_.values[i]) mutable_value_type; new (&fields_.values[i]) mutable_value_type;
} }
void value_init(int i, const value_type &x) { template <typename... Args>
new (&fields_.values[i]) mutable_value_type(x); void value_init_args(int i, Args&&... args) {
new (&fields_.values[i]) mutable_value_type(std::forward<Args>(args)...);
} }
void value_destroy(int i) { void value_destroy(int i) {
fields_.values[i].~mutable_value_type(); fields_.values[i].~mutable_value_type();
@@ -1002,45 +1008,65 @@ class btree : public Params::key_compare {
// Inserts a value into the btree only if it does not already exist. The // Inserts a value into the btree only if it does not already exist. The
// boolean return value indicates whether insertion succeeded or failed. The // boolean return value indicates whether insertion succeeded or failed. The
// ValuePointer type is used to avoid instatiating the value unless the key // Args&&... type is used to avoid instatiating the value unless the key
// is being inserted. Value is not dereferenced if the key already exists in // is being inserted. See btree_map::operator[].
// the btree. See btree_map::operator[]. template <typename... Args>
template <typename ValuePointer> std::pair<iterator,bool> insert_unique_args(const key_type &key, Args&&... args);
std::pair<iterator,bool> insert_unique(const key_type &key, ValuePointer value);
// Inserts a value into the btree only if it does not already exist. The // Inserts a value into the btree only if it does not already exist. The
// boolean return value indicates whether insertion succeeded or failed. // boolean return value indicates whether insertion succeeded or failed.
std::pair<iterator,bool> insert_unique(const value_type &v) { std::pair<iterator,bool> insert_unique(const value_type &v) {
return insert_unique(params_type::key(v), &v); return insert_unique_args(params_type::key(v), v);
} }
std::pair<iterator,bool> insert_unique(value_type &&v) {
return insert_unique_args(params_type::key(v), std::move(v));
}
template <typename... Args>
iterator insert_unique_hint_args(iterator position, const key_type &key, Args&&... args);
// Insert with hint. Check to see if the value should be placed immediately // Insert with hint. Check to see if the value should be placed immediately
// before position in the tree. If it does, then the insertion will take // before position in the tree. If it does, then the insertion will take
// amortized constant time. If not, the insertion will take amortized // amortized constant time. If not, the insertion will take amortized
// logarithmic time as if a call to insert_unique(v) were made. // logarithmic time as if a call to insert_unique(v) were made.
iterator insert_unique(iterator position, const value_type &v); iterator insert_unique(iterator position, const value_type &v) {
return insert_unique_hint_args(position, params_type::key(v), v);
}
iterator insert_unique(iterator position, value_type &&v) {
return insert_unique_hint_args(position, params_type::key(v), std::move(v));
}
// Insert a range of values into the btree. // Insert a range of values into the btree.
template <typename InputIterator> template <typename InputIterator>
void insert_unique(InputIterator b, InputIterator e); void insert_unique(InputIterator b, InputIterator e);
// Inserts a value into the btree. The ValuePointer type is used to avoid // Inserts a value into the btree. The Args&&... type is used to avoid
// instatiating the value unless the key is being inserted. Value is not // instatiating the value unless the key is being inserted. See
// dereferenced if the key already exists in the btree. See
// btree_map::operator[]. // btree_map::operator[].
template <typename ValuePointer> template <typename... Args>
iterator insert_multi(const key_type &key, ValuePointer value); iterator insert_multi_args(const key_type &key, Args&&... args);
// Inserts a value into the btree. // Inserts a value into the btree.
iterator insert_multi(const value_type &v) { iterator insert_multi(const value_type &v) {
return insert_multi(params_type::key(v), &v); return insert_multi_args(params_type::key(v), v);
} }
iterator insert_multi(value_type &&v) {
return insert_multi_args(params_type::key(v), std::move(v));
}
template <typename... Args>
iterator insert_multi_hint_args(iterator position, const key_type &key, Args&&... args);
// Insert with hint. Check to see if the value should be placed immediately // Insert with hint. Check to see if the value should be placed immediately
// before position in the tree. If it does, then the insertion will take // before position in the tree. If it does, then the insertion will take
// amortized constant time. If not, the insertion will take amortized // amortized constant time. If not, the insertion will take amortized
// logarithmic time as if a call to insert_multi(v) were made. // logarithmic time as if a call to insert_multi(v) were made.
iterator insert_multi(iterator position, const value_type &v); iterator insert_multi(iterator position, const value_type &v) {
return insert_multi_hint_args(position, params_type::key(v), v);
}
iterator insert_multi(iterator position, value_type &&v) {
return insert_multi_hint_args(position, params_type::key(v), std::move(v));
}
// Insert a range of values into the btree. // Insert a range of values into the btree.
template <typename InputIterator> template <typename InputIterator>
@@ -1305,9 +1331,12 @@ class btree : public Params::key_compare {
return iter.node ? iter : end(); return iter.node ? iter : end();
} }
iterator internal_insert_common(iterator iter);
// Inserts a value into the btree immediately before iter. Requires that // Inserts a value into the btree immediately before iter. Requires that
// key(v) <= iter.key() and (--iter).key() <= key(v). // key(v) <= iter.key() and (--iter).key() <= key(v).
iterator internal_insert(iterator iter, const value_type &v); template <typename... Args>
iterator internal_insert(iterator iter, Args&&... args);
// Returns an iterator pointing to the first value >= the value "iter" is // Returns an iterator pointing to the first value >= the value "iter" is
// pointing at. Note that "iter" might be pointing to an invalid location as // pointing at. Note that "iter" might be pointing to an invalid location as
@@ -1420,10 +1449,15 @@ class btree : public Params::key_compare {
//// ////
// btree_node methods // btree_node methods
template <typename P> template <typename P> template <typename... Args>
inline void btree_node<P>::insert_value(int i, const value_type &x) { inline void btree_node<P>::insert_value(int i, Args&&... args) {
dbg_assert(i <= count()); dbg_assert(i <= count());
value_init(count(), x); value_init_args(count(), std::forward<Args>(args)...);
insert_value_common(i);
}
template <typename P>
inline void btree_node<P>::insert_value_common(int i) {
for (int j = count(); j > i; --j) { for (int j = count(); j > i; --j) {
value_swap(j, this, j - 1); value_swap(j, this, j - 1);
} }
@@ -1576,7 +1610,7 @@ void btree_node<P>::split(btree_node *dest, int insert_position) {
// The split key is the largest value in the left sibling. // The split key is the largest value in the left sibling.
set_count(count() - 1); set_count(count() - 1);
parent()->insert_value(position(), value_type()); parent()->insert_value(position());
value_swap(count(), parent(), position()); value_swap(count(), parent(), position());
value_destroy(count()); value_destroy(count());
parent()->set_child(position() + 1, dest); parent()->set_child(position() + 1, dest);
@@ -1741,9 +1775,9 @@ btree<P>::btree(const self_type &x)
assign(x); assign(x);
} }
template <typename P> template <typename ValuePointer> template <typename P> template <typename... Args>
std::pair<typename btree<P>::iterator, bool> std::pair<typename btree<P>::iterator, bool>
btree<P>::insert_unique(const key_type &key, ValuePointer value) { btree<P>::insert_unique_args(const key_type &key, Args&&... args) {
if (empty()) { if (empty()) {
*mutable_root() = new_leaf_root_node(1); *mutable_root() = new_leaf_root_node(1);
} }
@@ -1761,34 +1795,33 @@ btree<P>::insert_unique(const key_type &key, ValuePointer value) {
} }
} }
return std::make_pair(internal_insert(iter, *value), true); return std::make_pair(internal_insert(iter, std::forward<Args>(args)...), true);
} }
template <typename P> template <typename P> template <typename... Args>
inline typename btree<P>::iterator inline typename btree<P>::iterator
btree<P>::insert_unique(iterator position, const value_type &v) { btree<P>::insert_unique_hint_args(iterator position, const key_type &key, Args&&... args) {
if (!empty()) { if (!empty()) {
const key_type &key = params_type::key(v);
const iterator end = this->end(); const iterator end = this->end();
if (position == end || compare_keys(key, position.key())) { if (position == end || compare_keys(key, position.key())) {
iterator prev = position; iterator prev = position;
if (position == begin() || compare_keys((--prev).key(), key)) { if (position == begin() || compare_keys((--prev).key(), key)) {
// prev.key() < key < position.key() // prev.key() < key < position.key()
return internal_insert(position, v); return internal_insert(position, std::forward<Args>(args)...);
} }
} else if (compare_keys(position.key(), key)) { } else if (compare_keys(position.key(), key)) {
iterator next = position; iterator next = position;
++next; ++next;
if (next == end || compare_keys(key, next.key())) { if (next == end || compare_keys(key, next.key())) {
// position.key() < key < next.key() // position.key() < key < next.key()
return internal_insert(next, v); return internal_insert(next, std::forward<Args>(args)...);
} }
} else { } else {
// position.key() == key // position.key() == key
return position; return position;
} }
} }
return insert_unique(v).first; return insert_unique_args(key, std::forward<Args>(args)...).first;
} }
template <typename P> template <typename InputIterator> template <typename P> template <typename InputIterator>
@@ -1798,9 +1831,9 @@ void btree<P>::insert_unique(InputIterator b, InputIterator e) {
} }
} }
template <typename P> template <typename ValuePointer> template <typename P> template <typename... Args>
typename btree<P>::iterator typename btree<P>::iterator
btree<P>::insert_multi(const key_type &key, ValuePointer value) { btree<P>::insert_multi_args(const key_type &key, Args&&... args) {
if (empty()) { if (empty()) {
*mutable_root() = new_leaf_root_node(1); *mutable_root() = new_leaf_root_node(1);
} }
@@ -1809,31 +1842,31 @@ btree<P>::insert_multi(const key_type &key, ValuePointer value) {
if (!iter.node) { if (!iter.node) {
iter = end(); iter = end();
} }
return internal_insert(iter, *value); return internal_insert(iter, std::forward<Args>(args)...);
} }
template <typename P>
template <typename P> template <typename... Args>
typename btree<P>::iterator typename btree<P>::iterator
btree<P>::insert_multi(iterator position, const value_type &v) { btree<P>::insert_multi_hint_args(iterator position, const key_type &key, Args&&... args) {
if (!empty()) { if (!empty()) {
const key_type &key = params_type::key(v);
const iterator end = this->end(); const iterator end = this->end();
if (position == end || !compare_keys(position.key(), key)) { if (position == end || !compare_keys(position.key(), key)) {
iterator prev = position; iterator prev = position;
if (position == begin() || !compare_keys(key, (--prev).key())) { if (position == begin() || !compare_keys(key, (--prev).key())) {
// prev.key() <= key <= position.key() // prev.key() <= key <= position.key()
return internal_insert(position, v); return internal_insert(position, std::forward<Args>(args)...);
} }
} else { } else {
iterator next = position; iterator next = position;
++next; ++next;
if (next == end || !compare_keys(next.key(), key)) { if (next == end || !compare_keys(next.key(), key)) {
// position.key() < key <= next.key() // position.key() < key <= next.key()
return internal_insert(next, v); return internal_insert(next, std::forward<Args>(args)...);
} }
} }
} }
return insert_multi(v); return insert_multi_args(key, std::forward<Args>(args)...);
} }
template <typename P> template <typename InputIterator> template <typename P> template <typename InputIterator>
@@ -2202,7 +2235,7 @@ inline IterType btree<P>::internal_last(IterType iter) {
template <typename P> template <typename P>
inline typename btree<P>::iterator inline typename btree<P>::iterator
btree<P>::internal_insert(iterator iter, const value_type &v) { btree<P>::internal_insert_common(iterator iter) {
if (!iter.node->leaf()) { if (!iter.node->leaf()) {
// We can't insert on an internal node. Instead, we'll insert after the // We can't insert on an internal node. Instead, we'll insert after the
// previous value which is guaranteed to be on a leaf node. // previous value which is guaranteed to be on a leaf node.
@@ -2227,7 +2260,14 @@ btree<P>::internal_insert(iterator iter, const value_type &v) {
} else if (!root()->leaf()) { } else if (!root()->leaf()) {
++*mutable_size(); ++*mutable_size();
} }
iter.node->insert_value(iter.position, v); return iter;
}
template <typename P> template <typename... Args>
inline typename btree<P>::iterator
btree<P>::internal_insert(iterator iter, Args&&... args) {
iter = internal_insert_common(iter);
iter.node->insert_value(iter.position, std::forward<Args>(args)...);
return iter; return iter;
} }

View File

@@ -201,9 +201,15 @@ class btree_unique_container : public btree_container<Tree> {
std::pair<iterator,bool> insert(const value_type &x) { std::pair<iterator,bool> insert(const value_type &x) {
return this->tree_.insert_unique(x); return this->tree_.insert_unique(x);
} }
std::pair<iterator,bool> insert(value_type &&x) {
return this->tree_.insert_unique(std::move(x));
}
iterator insert(iterator position, const value_type &x) { iterator insert(iterator position, const value_type &x) {
return this->tree_.insert_unique(position, x); return this->tree_.insert_unique(position, x);
} }
iterator insert(iterator position, value_type &&x) {
return this->tree_.insert_unique(position, std::move(x));
}
template <typename InputIterator> template <typename InputIterator>
void insert(InputIterator b, InputIterator e) { void insert(InputIterator b, InputIterator e) {
this->tree_.insert_unique(b, e); this->tree_.insert_unique(b, e);
@@ -238,21 +244,6 @@ class btree_map_container : public btree_unique_container<Tree> {
typedef typename Tree::key_compare key_compare; typedef typename Tree::key_compare key_compare;
typedef typename Tree::allocator_type allocator_type; typedef typename Tree::allocator_type allocator_type;
private:
// A pointer-like object which only generates its value when
// dereferenced. Used by operator[] to avoid constructing an empty data_type
// if the key already exists in the map.
struct generate_value {
generate_value(const key_type &k)
: key(k) {
}
value_type operator*() const {
return std::make_pair(key, data_type());
}
const key_type &key;
};
public:
// Default constructor. // Default constructor.
btree_map_container(const key_compare &comp = key_compare(), btree_map_container(const key_compare &comp = key_compare(),
const allocator_type &alloc = allocator_type()) const allocator_type &alloc = allocator_type())
@@ -274,7 +265,7 @@ class btree_map_container : public btree_unique_container<Tree> {
// Insertion routines. // Insertion routines.
data_type& operator[](const key_type &key) { data_type& operator[](const key_type &key) {
return this->tree_.insert_unique(key, generate_value(key)).first->second; return this->tree_.insert_unique_args(key, std::piecewise_construct, std::forward_as_tuple(key), std::make_tuple()).first->second;
} }
}; };
@@ -329,9 +320,15 @@ class btree_multi_container : public btree_container<Tree> {
iterator insert(const value_type &x) { iterator insert(const value_type &x) {
return this->tree_.insert_multi(x); return this->tree_.insert_multi(x);
} }
iterator insert(value_type &&x) {
return this->tree_.insert_multi(std::move(x));
}
iterator insert(iterator position, const value_type &x) { iterator insert(iterator position, const value_type &x) {
return this->tree_.insert_multi(position, x); return this->tree_.insert_multi(position, x);
} }
iterator insert(iterator position, value_type &&x) {
return this->tree_.insert_multi(position, std::move(x));
}
template <typename InputIterator> template <typename InputIterator>
void insert(InputIterator b, InputIterator e) { void insert(InputIterator b, InputIterator e) {
this->tree_.insert_multi(b, e); this->tree_.insert_multi(b, e);

View File

@@ -274,36 +274,64 @@ class safe_btree {
} }
// Insertion routines. // Insertion routines.
template <typename ValuePointer> template <typename... Args>
std::pair<iterator, bool> insert_unique(const key_type &key, ValuePointer value) { std::pair<iterator,bool> insert_unique_args(const key_type &key, Args&&... args) {
std::pair<tree_iterator, bool> p = tree_.insert_unique(key, value); std::pair<tree_iterator, bool> p = tree_.insert_unique_args(key, std::forward<Args>(args)...);
generation_ += p.second; generation_ += p.second;
return std::make_pair(iterator(this, p.first), p.second); return std::make_pair(iterator(this, p.first), p.second);
} }
std::pair<iterator, bool> insert_unique(const value_type &v) { std::pair<iterator,bool> insert_unique(const value_type &v) {
std::pair<tree_iterator, bool> p = tree_.insert_unique(v); return insert_unique_args(params_type::key(v), v);
generation_ += p.second;
return std::make_pair(iterator(this, p.first), p.second);
} }
iterator insert_unique(iterator position, const value_type &v) { std::pair<iterator,bool> insert_unique(value_type &&v) {
return insert_unique_args(params_type::key(v), std::move(v));
}
template <typename... Args>
iterator insert_unique_hint_args(iterator position, const key_type &key, Args&&... args) {
tree_iterator tree_pos = position.iter(); tree_iterator tree_pos = position.iter();
++generation_; ++generation_;
return iterator(this, tree_.insert_unique(tree_pos, v)); return iterator(this, tree_.insert_unique_hint_args(tree_pos, key, std::forward<Args>(args)...));
} }
iterator insert_unique(iterator position, const value_type &v) {
return insert_unique_hint_args(position, params_type::key(v), v);
}
iterator insert_unique(iterator position, value_type &&v) {
return insert_unique_hint_args(position, params_type::key(v), std::move(v));
}
template <typename InputIterator> template <typename InputIterator>
void insert_unique(InputIterator b, InputIterator e) { void insert_unique(InputIterator b, InputIterator e) {
for (; b != e; ++b) { for (; b != e; ++b) {
insert_unique(*b); insert_unique(*b);
} }
} }
iterator insert_multi(const value_type &v) {
template <typename... Args>
iterator insert_multi_args(const key_type &key, Args&&... args) {
++generation_; ++generation_;
return iterator(this, tree_.insert_multi(v)); return iterator(this, tree_.insert_multi_args(key, std::forward<Args>(args)...));
} }
iterator insert_multi(iterator position, const value_type &v) {
iterator insert_multi(const value_type &v) {
return insert_multi_args(params_type::key(v), v);
}
iterator insert_multi(value_type &&v) {
return insert_multi_args(params_type::key(v), std::move(v));
}
template <typename... Args>
iterator insert_multi_hint_args(iterator position, const key_type &key, Args&&... args) {
tree_iterator tree_pos = position.iter(); tree_iterator tree_pos = position.iter();
++generation_; ++generation_;
return iterator(this, tree_.insert_multi(tree_pos, v)); return iterator(this, tree_.insert_multi_hint_args(tree_pos, key, std::forward<Args>(args)...));
}
iterator insert_multi(iterator position, const value_type &v) {
return insert_multi_hint_args(position, params_type::key(v), v);
}
iterator insert_multi(iterator position, value_type &&v) {
return insert_multi_hint_args(position, params_type::key(v), std::move(v));
} }
template <typename InputIterator> template <typename InputIterator>
void insert_multi(InputIterator b, InputIterator e) { void insert_multi(InputIterator b, InputIterator e) {