BLI: destruct non-trivial types after using remove_if

`Vector::remove_if` allows certain elements to be removed based on a predicate.
However `std::remove_if` only shifts the elements that will not be deleted
to the beginning of the container and then `Vector::remove_if` only
updates the pointer to the new last element after using `std::remove_if`.
This works well if `Vector` is used with trivial types that don't have a destructor.

Having a `Vector<std::unique_ptr>` for example, can generate undefined behavior
if the predicate gives `true` to elements that are contiguous at the end, if
`Vector::remove_if` only updates the end pointer in these specific cases, these
makes these smart pointers useless because  they will not be freed by themselves.

To prevent that, also destruct the elements being removed.

Pull Request: https://projects.blender.org/blender/blender/pulls/115914
This commit is contained in:
Guillermo Venegas 2023-12-12 10:42:04 +01:00 committed by Jacques Lucke
parent efc1094d6a
commit e23b8eeb42
2 changed files with 12 additions and 0 deletions

View File

@ -816,6 +816,7 @@ class Vector {
{
const T *prev_end = this->end();
end_ = std::remove_if(this->begin(), this->end(), predicate);
destruct_n(end_, prev_end - end_);
UPDATE_VECTOR_SIZE(this);
return int64_t(prev_end - end_);
}

View File

@ -431,6 +431,17 @@ TEST(vector, RemoveIf)
EXPECT_EQ_ARRAY(vec.data(), expected_vec.data(), size_t(vec.size()));
}
TEST(vector, RemoveIfNonTrivialDestructible)
{
Vector<Vector<int, 0, GuardedAllocator>> vec;
for ([[maybe_unused]] const int64_t i : IndexRange(10)) {
/* This test relies on leak detection to run after tests. */
vec.append(Vector<int, 0, GuardedAllocator>(100));
}
vec.remove_if([&](const auto & /*value*/) { return true; });
EXPECT_TRUE(vec.is_empty());
}
TEST(vector, ExtendSmallVector)
{
Vector<int> a = {2, 3, 4};