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

pull/544/head
Jonathan G Rennison 1 year ago
parent b381a30d2d
commit f00e0e195f

@ -40,6 +40,9 @@ Changes include:
- Adding key_comp
- Adding move constructors/assignment.
- 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.
Using this instead fixes compilation on some platforms.

@ -649,9 +649,14 @@ class btree_node {
return s;
}
private:
void insert_value_common(int i);
public:
// Inserts the value x at position i, shifting all existing values and
// 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
// at positions > i to the left by 1.
@ -709,8 +714,9 @@ class btree_node {
void value_init(int i) {
new (&fields_.values[i]) mutable_value_type;
}
void value_init(int i, const value_type &x) {
new (&fields_.values[i]) mutable_value_type(x);
template <typename... Args>
void value_init_args(int i, Args&&... args) {
new (&fields_.values[i]) mutable_value_type(std::forward<Args>(args)...);
}
void value_destroy(int i) {
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
// boolean return value indicates whether insertion succeeded or failed. The
// ValuePointer type is used to avoid instatiating the value unless the key
// is being inserted. Value is not dereferenced if the key already exists in
// the btree. See btree_map::operator[].
template <typename ValuePointer>
std::pair<iterator,bool> insert_unique(const key_type &key, ValuePointer value);
// Args&&... type is used to avoid instatiating the value unless the key
// is being inserted. See btree_map::operator[].
template <typename... Args>
std::pair<iterator,bool> insert_unique_args(const key_type &key, Args&&... args);
// Inserts a value into the btree only if it does not already exist. The
// boolean return value indicates whether insertion succeeded or failed.
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
// before position in the tree. If it does, then the insertion will take
// amortized constant time. If not, the insertion will take amortized
// 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.
template <typename InputIterator>
void insert_unique(InputIterator b, InputIterator e);
// Inserts a value into the btree. The ValuePointer type is used to avoid
// instatiating the value unless the key is being inserted. Value is not
// dereferenced if the key already exists in the btree. See
// Inserts a value into the btree. The Args&&... type is used to avoid
// instatiating the value unless the key is being inserted. See
// btree_map::operator[].
template <typename ValuePointer>
iterator insert_multi(const key_type &key, ValuePointer value);
template <typename... Args>
iterator insert_multi_args(const key_type &key, Args&&... args);
// Inserts a value into the btree.
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
// before position in the tree. If it does, then the insertion will take
// amortized constant time. If not, the insertion will take amortized
// 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.
template <typename InputIterator>
@ -1305,9 +1331,12 @@ class btree : public Params::key_compare {
return iter.node ? iter : end();
}
iterator internal_insert_common(iterator iter);
// Inserts a value into the btree immediately before iter. Requires that
// 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
// 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
template <typename P>
inline void btree_node<P>::insert_value(int i, const value_type &x) {
template <typename P> template <typename... Args>
inline void btree_node<P>::insert_value(int i, Args&&... args) {
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) {
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.
set_count(count() - 1);
parent()->insert_value(position(), value_type());
parent()->insert_value(position());
value_swap(count(), parent(), position());
value_destroy(count());
parent()->set_child(position() + 1, dest);
@ -1741,9 +1775,9 @@ btree<P>::btree(const self_type &x)
assign(x);
}
template <typename P> template <typename ValuePointer>
template <typename P> template <typename... Args>
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()) {
*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
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()) {
const key_type &key = params_type::key(v);
const iterator end = this->end();
if (position == end || compare_keys(key, position.key())) {
iterator prev = position;
if (position == begin() || compare_keys((--prev).key(), 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)) {
iterator next = position;
++next;
if (next == end || compare_keys(key, next.key())) {
// position.key() < key < next.key()
return internal_insert(next, v);
return internal_insert(next, std::forward<Args>(args)...);
}
} else {
// position.key() == key
return position;
}
}
return insert_unique(v).first;
return insert_unique_args(key, std::forward<Args>(args)...).first;
}
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
btree<P>::insert_multi(const key_type &key, ValuePointer value) {
btree<P>::insert_multi_args(const key_type &key, Args&&... args) {
if (empty()) {
*mutable_root() = new_leaf_root_node(1);
}
@ -1809,31 +1842,31 @@ btree<P>::insert_multi(const key_type &key, ValuePointer value) {
if (!iter.node) {
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
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()) {
const key_type &key = params_type::key(v);
const iterator end = this->end();
if (position == end || !compare_keys(position.key(), key)) {
iterator prev = position;
if (position == begin() || !compare_keys(key, (--prev).key())) {
// prev.key() <= key <= position.key()
return internal_insert(position, v);
return internal_insert(position, std::forward<Args>(args)...);
}
} else {
iterator next = position;
++next;
if (next == end || !compare_keys(next.key(), 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>
@ -2202,7 +2235,7 @@ inline IterType btree<P>::internal_last(IterType iter) {
template <typename P>
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()) {
// 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.
@ -2227,7 +2260,14 @@ btree<P>::internal_insert(iterator iter, const value_type &v) {
} else if (!root()->leaf()) {
++*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;
}

@ -201,9 +201,15 @@ class btree_unique_container : public btree_container<Tree> {
std::pair<iterator,bool> insert(const value_type &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) {
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>
void insert(InputIterator b, InputIterator 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::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.
btree_map_container(const key_compare &comp = key_compare(),
const allocator_type &alloc = allocator_type())
@ -274,7 +265,7 @@ class btree_map_container : public btree_unique_container<Tree> {
// Insertion routines.
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) {
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) {
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>
void insert(InputIterator b, InputIterator e) {
this->tree_.insert_multi(b, e);

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

Loading…
Cancel
Save