Core/StackVector: Fix erase method (+ add tests)

This commit is contained in:
Lynix 2019-09-25 16:17:07 +02:00
parent 3290c497e7
commit acc2c072ba
2 changed files with 190 additions and 37 deletions

View File

@ -123,14 +123,19 @@ namespace Nz
assert(m_size < m_capacity);
assert(pos >= begin() && pos <= end());
std::size_t index = std::distance<const_iterator>(begin(), pos);
std::size_t index = std::distance(cbegin(), pos);
if (pos < end())
{
iterator lastElement = end() - 1;
PlacementNew(&m_ptr[m_size], std::move(*lastElement));
if (&m_ptr[index] < lastElement)
std::move(&m_ptr[index], lastElement, &m_ptr[index + 1]);
{
std::size_t count = m_size - index - 1;
std::move_backward(&m_ptr[index], &m_ptr[index + count], &m_ptr[m_size]);
}
PlacementDestroy(&m_ptr[index]);
}
m_size++;
@ -167,8 +172,8 @@ namespace Nz
typename StackVector<T>::iterator StackVector<T>::erase(const_iterator pos)
{
assert(pos < end());
std::size_t index = std::distance(begin(), pos);
std::move(pos + 1, end(), pos);
std::size_t index = std::distance(cbegin(), pos);
std::move(begin() + index + 1, end(), begin() + index);
pop_back();
return iterator(&m_ptr[index]);
@ -177,15 +182,18 @@ namespace Nz
template<typename T>
typename StackVector<T>::iterator StackVector<T>::erase(const_iterator first, const_iterator last)
{
std::size_t index = std::distance(cbegin(), first);
if (first == last)
return first;
return begin() + index;
assert(first < last);
assert(first >= begin() && last <= end());
std::size_t index = std::distance(begin(), first);
std::move(last, end(), first);
resize(size() - (last - first));
std::size_t count = std::distance(first, last);
std::move(begin() + index + count, end(), begin() + index);
resize(size() - count);
return iterator(&m_ptr[index]);
}

View File

@ -1,14 +1,113 @@
#include <Nazara/Core/MovablePtr.hpp>
#include <Nazara/Core/StackVector.hpp>
#include <Catch/catch.hpp>
#include <array>
#include <numeric>
// This is a quick way to check that checks are valid
#define USE_STD_VECTOR 0
class DestructionCounter
{
public:
DestructionCounter() :
m_counter(nullptr),
m_value(0)
{
}
DestructionCounter(std::size_t* counter, int value) :
m_counter(counter),
m_value(value)
{
if (m_counter)
(*m_counter)++;
}
DestructionCounter(const DestructionCounter& counter) :
m_counter(counter.m_counter),
m_value(counter.m_value)
{
if (m_counter)
(*m_counter)++;
}
DestructionCounter(DestructionCounter&& counter) :
m_counter(counter.m_counter),
m_value(counter.m_value)
{
if (m_counter)
(*m_counter)++;
}
~DestructionCounter()
{
if (m_counter)
{
assert(*m_counter > 0);
(*m_counter)--;
}
}
operator int() const
{
return m_value;
}
DestructionCounter& operator=(const DestructionCounter& counter)
{
if (m_counter)
{
assert(*m_counter > 0);
(*m_counter)--;
}
m_counter = counter.m_counter;
m_value = counter.m_value;
if (m_counter)
(*m_counter)++;
return *this;
}
DestructionCounter& operator=(DestructionCounter&& counter)
{
if (this == &counter)
return *this;
if (m_counter)
{
assert(*m_counter > 0);
(*m_counter)--;
}
m_counter = counter.m_counter;
m_value = counter.m_value;
if (m_counter)
(*m_counter)++;
return *this;
}
private:
std::size_t* m_counter;
int m_value;
};
SCENARIO("StackVector", "[CORE][STACKVECTOR]")
{
GIVEN("A StackVector to contain multiple int")
{
volatile std::size_t capacity = 50;
Nz::StackVector<int> vector = NazaraStackVector(int, capacity);
#if USE_STD_VECTOR
std::vector<DestructionCounter> vector;
vector.reserve(capacity);
#else
Nz::StackVector<DestructionCounter> vector = NazaraStackVector(DestructionCounter, capacity);
#endif
std::size_t counter = 0;
WHEN("At construction, the vector is empty but has capacity")
{
@ -21,8 +120,11 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]")
{
for (std::size_t i = 0; i < 5; ++i)
{
int val = int(i);
CHECK(vector.emplace_back(val) == val);
#if USE_STD_VECTOR
vector.emplace_back(&counter, int(i));
#else
CHECK(vector.emplace_back(&counter, int(i)) == int(i));
#endif
}
CHECK(!vector.empty());
@ -36,8 +138,12 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]")
{
for (std::size_t i = 0; i < 3; ++i)
{
int val = int(i);
DestructionCounter val(&counter, int(i));
#if USE_STD_VECTOR
vector.push_back(val);
#else
CHECK(vector.push_back(val) == val);
#endif
}
CHECK(!vector.empty());
@ -76,19 +182,70 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]")
WHEN("We generate its content will iota")
{
vector.resize(10);
std::iota(vector.begin(), vector.end(), -5);
for (std::size_t i = 0; i < vector.size(); ++i)
vector[i] = DestructionCounter(&counter, -5 + int(i));
std::array<int, 10> expectedValues = { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
AND_WHEN("We pop back some elements")
{
for (std::size_t i = 0; i < 5; ++i)
vector.pop_back();
std::array<int, 5> expectedValues = { -5, -4, -3, -2, -1 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We erase elements at the beginning")
{
vector.erase(vector.begin());
vector.erase(vector.begin());
std::array<int, 8> expectedValues = { -3, -2, -1, 0, 1, 2, 3, 4 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We erase elements in the middle")
{
vector.erase(vector.begin() + 2);
vector.erase(vector.begin() + 2);
vector.erase(vector.begin() + 6);
std::array<int, 7> expectedValues = { -5, -4, -1, 0, 1, 2, 4 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We erase elements at the end")
{
vector.erase(vector.end() - 1);
vector.erase(vector.end() - 1);
std::array<int, 8> expectedValues = { -5, -4, -3, -2, -1, 0, 1, 2 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We erase a range")
{
vector.erase(vector.begin() + 2, vector.end() - 3);
std::array<int, 5> expectedValues = { -5, -4, 2, 3, 4 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We erase everything")
{
vector.erase(vector.begin(), vector.end());
CHECK(vector.empty());
}
}
WHEN("We generate its content using emplace")
{
for (std::size_t i = 0; i < 5; ++i)
{
int val = int(i);
CHECK(*vector.emplace(vector.end(), val) == val);
}
CHECK(*vector.emplace(vector.end(), &counter, int(i)) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 5);
@ -100,10 +257,7 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]")
WHEN("We generate its content using emplace, in reverse order")
{
for (std::size_t i = 0; i < 5; ++i)
{
int val = int(i);
CHECK(*vector.emplace(vector.begin(), val) == val);
}
CHECK(*vector.emplace(vector.begin(), &counter, int(i)) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 5);
@ -115,10 +269,7 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]")
WHEN("We generate its content using emplace, at the middle")
{
for (std::size_t i = 0; i < 10; ++i)
{
int val = int(i);
CHECK(*vector.emplace(vector.begin() + i / 2, val) == val);
}
CHECK(*vector.emplace(vector.begin() + i / 2, &counter, int(i)) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 10);
@ -130,10 +281,7 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]")
WHEN("We generate its content using insert")
{
for (std::size_t i = 0; i < 5; ++i)
{
int val = int(i);
CHECK(*vector.insert(vector.end(), val) == val);
}
CHECK(*vector.insert(vector.end(), DestructionCounter(&counter, int(i))) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 5);
@ -145,10 +293,7 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]")
WHEN("We generate its content using insert, in reverse order")
{
for (std::size_t i = 0; i < 5; ++i)
{
int val = int(i);
CHECK(*vector.insert(vector.begin(), val) == val);
}
CHECK(*vector.insert(vector.begin(), DestructionCounter(&counter, int(i))) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 5);
@ -160,10 +305,7 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]")
WHEN("We generate its content using insert, at the middle")
{
for (std::size_t i = 0; i < 10; ++i)
{
int val = int(i);
CHECK(*vector.insert(vector.begin() + i / 2, val) == val);
}
CHECK(*vector.insert(vector.begin() + i / 2, DestructionCounter(&counter, int(i))) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 10);
@ -171,5 +313,8 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]")
std::array<int, 10> expectedValues = { 1, 3, 5, 7, 9, 8, 6, 4, 2, 0 };
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
vector.clear();
CHECK(counter == 0);
}
}