Skip to content

Commit

Permalink
Add a slot_map::partition(Pred) member function.
Browse files Browse the repository at this point in the history
Just like the standard `std::partition` algorithm, this member function
reorders the slot_map's elements in such a way that all elements for which
the predicate `pred` returns `true` precede the elements for which `pred`
returns `false`. The relative order of the elements is not preserved.

The function returns an iterator to the first element of the second group
(which may be `end()`, in the case that all elements belong to the first group).

Addresses part of WG21-SG14#131, although I'm not sure whether this really addresses
whatever use case @p-groarke is thinking of.
  • Loading branch information
Quuxplusone committed Feb 17, 2023
1 parent c923070 commit 1de89ab
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
34 changes: 34 additions & 0 deletions include/sg14/slot_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,40 @@ class slot_map
return 1;
}

template<class Pred>
constexpr iterator partition(const Pred& pred) {
iterator it = this->begin();
iterator jt = this->end();
if (it == jt) return it;
while (true) {
--jt;
if (it == jt) return it;
while (pred(*it)) {
++it;
if (it == jt) return it;
}
while (!pred(*jt)) {
--jt;
if (it == jt) return it;
}
// Swap *it and *jt in the underlying container,
// but then fix up their keys so they don't appear to move.
auto it_value_index = std::distance(values_.begin(), it);
auto it_reversemap_iter = std::next(reverse_map_.begin(), it_value_index);
auto it_slot_iter = std::next(slots_.begin(), *it_reversemap_iter);
auto jt_value_index = std::distance(values_.begin(), jt);
auto jt_reversemap_iter = std::next(reverse_map_.begin(), jt_value_index);
auto jt_slot_iter = std::next(slots_.begin(), *jt_reversemap_iter);

using std::swap;
swap(*it, *jt);
swap(*it_slot_iter, *jt_slot_iter);
swap(*it_reversemap_iter, *jt_reversemap_iter);
++it;
if (it == jt) return it;
}
}

// clear() has O(n) time complexity and O(1) space complexity.
// It also has semantics differing from erase(begin(), end())
// in that it also resets the generation counter of every slot
Expand Down
40 changes: 40 additions & 0 deletions test/slot_map_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,39 @@ static void EraseRangeTest()
}
}

template<class SM>
static void PartitionTest()
{
using T = typename SM::mapped_type;
SM sm;
auto key3 = sm.insert(Monad<T>::from_value(3));
auto key1 = sm.insert(Monad<T>::from_value(1));
auto key4 = sm.insert(Monad<T>::from_value(4));
auto key5 = sm.insert(Monad<T>::from_value(5));
auto key9 = sm.insert(Monad<T>::from_value(9));
auto key2 = sm.insert(Monad<T>::from_value(2));
auto key6 = sm.insert(Monad<T>::from_value(6));

auto pivot = sm.partition([](const auto& elt) {
return Monad<T>::value_of(elt) >= 5;
});

for (auto it = sm.begin(); it != pivot; ++it) {
assert(Monad<T>::value_of(*it) >= 5);
}
for (auto it = pivot; it != sm.end(); ++it) {
assert(Monad<T>::value_of(*it) < 5);
}

assert(Monad<T>::value_of(*sm.find(key3)) == 3);
assert(Monad<T>::value_of(*sm.find(key1)) == 1);
assert(Monad<T>::value_of(*sm.find(key4)) == 4);
assert(Monad<T>::value_of(*sm.find(key5)) == 5);
assert(Monad<T>::value_of(*sm.find(key9)) == 9);
assert(Monad<T>::value_of(*sm.find(key2)) == 2);
assert(Monad<T>::value_of(*sm.find(key6)) == 6);
}

template<class SM>
static void ReserveTest()
{
Expand Down Expand Up @@ -523,6 +556,7 @@ TEST(slot_map, Basic)
InsertEraseStressTest<slot_map_1>([i=3]() mutable { return ++i; });
EraseInLoopTest<slot_map_1>();
EraseRangeTest<slot_map_1>();
PartitionTest<slot_map_1>();
ReserveTest<slot_map_1>();
VerifyCapacityExists<slot_map_1>(true);
GenerationsDontSkipTest<slot_map_1>();
Expand All @@ -539,6 +573,7 @@ TEST(slot_map, CustomKeyType)
InsertEraseStressTest<slot_map_2>([i=5]() mutable { return ++i; });
EraseInLoopTest<slot_map_2>();
EraseRangeTest<slot_map_2>();
PartitionTest<slot_map_2>();
ReserveTest<slot_map_2>();
VerifyCapacityExists<slot_map_2>(true);
GenerationsDontSkipTest<slot_map_2>();
Expand All @@ -553,6 +588,7 @@ TEST(slot_map, CustomKeyType)
InsertEraseStressTest<slot_map_3>([i=3]() mutable { return ++i; });
EraseInLoopTest<slot_map_3>();
EraseRangeTest<slot_map_3>();
PartitionTest<slot_map_3>();
ReserveTest<slot_map_3>();
VerifyCapacityExists<slot_map_3>(true);
GenerationsDontSkipTest<slot_map_3>();
Expand All @@ -570,6 +606,7 @@ TEST(slot_map, DequeContainer)
InsertEraseStressTest<slot_map_4>([i=7]() mutable { return ++i; });
EraseInLoopTest<slot_map_4>();
EraseRangeTest<slot_map_4>();
PartitionTest<slot_map_4>();
ReserveTest<slot_map_4>();
VerifyCapacityExists<slot_map_4>(false);
GenerationsDontSkipTest<slot_map_4>();
Expand All @@ -587,6 +624,7 @@ TEST(slot_map, CustomRAContainer)
InsertEraseStressTest<slot_map_5>([i=7]() mutable { return ++i; });
EraseInLoopTest<slot_map_5>();
EraseRangeTest<slot_map_5>();
PartitionTest<slot_map_5>();
ReserveTest<slot_map_5>();
VerifyCapacityExists<slot_map_5>(false);
GenerationsDontSkipTest<slot_map_5>();
Expand All @@ -605,6 +643,7 @@ TEST(slot_map, CustomBidiContainer)
InsertEraseStressTest<slot_map_6>([i=7]() mutable { return ++i; });
EraseInLoopTest<slot_map_6>();
EraseRangeTest<slot_map_6>();
PartitionTest<slot_map_6>();
ReserveTest<slot_map_6>();
VerifyCapacityExists<slot_map_6>(false);
GenerationsDontSkipTest<slot_map_6>();
Expand All @@ -626,6 +665,7 @@ TEST(slot_map, MoveOnlyValueType)
InsertEraseStressTest<slot_map_7>([i=7]() mutable { return std::make_unique<int>(++i); });
EraseInLoopTest<slot_map_7>();
EraseRangeTest<slot_map_7>();
PartitionTest<slot_map_7>();
ReserveTest<slot_map_7>();
VerifyCapacityExists<slot_map_7>(false);
GenerationsDontSkipTest<slot_map_7>();
Expand Down

0 comments on commit 1de89ab

Please sign in to comment.