#include "vector.h"
#include <stdexcept>
namespace {
// Магическое число, используемое для отслеживания живости объекта
inline const uint32_t DEFAULT_COOKIE = 0xdeadbeef;
struct TestObj {
TestObj() = default;
TestObj(const TestObj& other) = default;
TestObj& operator=(const TestObj& other) = default;
TestObj(TestObj&& other) = default;
TestObj& operator=(TestObj&& other) = default;
~TestObj() {
cookie = 0;
}
[[nodiscard]] bool IsAlive() const noexcept {
return cookie == DEFAULT_COOKIE;
}
uint32_t cookie = DEFAULT_COOKIE;
};
struct Obj {
Obj() {
if (default_construction_throw_countdown > 0) {
if (--default_construction_throw_countdown == 0) {
throw std::runtime_error("Oops");
}
}
++num_default_constructed;
}
explicit Obj(int id)
: id(id) //
{
++num_constructed_with_id;
}
Obj(const Obj& other)
: id(other.id) //
{
if (other.throw_on_copy) {
throw std::runtime_error("Oops");
}
++num_copied;
}
Obj(Obj&& other) noexcept
: id(other.id) //
{
++num_moved;
}
Obj& operator=(const Obj& other) = default;
Obj& operator=(Obj&& other) = default;
~Obj() {
++num_destroyed;
id = 0;
}
static int GetAliveObjectCount() {
return num_default_constructed + num_copied + num_moved + num_constructed_with_id
- num_destroyed;
}
static void ResetCounters() {
default_construction_throw_countdown = 0;
num_default_constructed = 0;
num_copied = 0;
num_moved = 0;
num_destroyed = 0;
num_constructed_with_id = 0;
}
bool throw_on_copy = false;
int id = 0;
static inline int default_construction_throw_countdown = 0;
static inline int num_default_constructed = 0;
static inline int num_constructed_with_id = 0;
static inline int num_copied = 0;
static inline int num_moved = 0;
static inline int num_destroyed = 0;
};
} // namespace
void Test1() {
Obj::ResetCounters();
const size_t SIZE = 100500;
const size_t INDEX = 10;
const int MAGIC = 42;
{
Vector<int> v;
assert(v.Capacity() == 0);
assert(v.Size() == 0);
v.Reserve(SIZE);
assert(v.Capacity() == SIZE);
assert(v.Size() == 0);
}
{
Vector<int> v(SIZE);
const auto& cv(v);
assert(v.Capacity() == SIZE);
assert(v.Size() == SIZE);
assert(v[0] == 0);
assert(&v[0] == &cv[0]);
v[INDEX] = MAGIC;
assert(v[INDEX] == MAGIC);
assert(&v[100] - &v[0] == 100);
v.Reserve(SIZE * 2);
assert(v.Size() == SIZE);
assert(v.Capacity() == SIZE * 2);
assert(v[INDEX] == MAGIC);
}
{
Vector<int> v(SIZE);
v[INDEX] = MAGIC;
const auto v_copy(v);
assert(&v[INDEX] != &v_copy[INDEX]);
assert(v[INDEX] == v_copy[INDEX]);
}
{
Vector<Obj> v;
v.Reserve(SIZE);
assert(Obj::GetAliveObjectCount() == 0);
}
{
Vector<Obj> v(SIZE);
assert(Obj::GetAliveObjectCount() == SIZE);
const int old_copy_count = Obj::num_copied;
const int old_move_count = Obj::num_moved;
v.Reserve(SIZE * 2);
assert(Obj::GetAliveObjectCount() == SIZE);
assert(Obj::num_copied == old_copy_count);
assert(Obj::num_moved == old_move_count + static_cast<int>(SIZE));
}
assert(Obj::GetAliveObjectCount() == 0);
}
void Test2() {
const size_t SIZE = 100;
Obj::ResetCounters();
{
Obj::default_construction_throw_countdown = SIZE / 2;
try {
Vector<Obj> v(SIZE);
assert(false && "Exception is expected");
} catch (const std::runtime_error&) {
} catch (...) {
// Unexpected error
assert(false && "Unexpected exception");
}
assert(Obj::num_default_constructed == SIZE / 2 - 1);
assert(Obj::GetAliveObjectCount() == 0);
}
Obj::ResetCounters();
{
Vector<Obj> v(SIZE);
try {
v[SIZE / 2].throw_on_copy = true;
Vector<Obj> v_copy(v);
assert(false && "Exception is expected");
} catch (const std::runtime_error&) {
assert(Obj::num_copied == SIZE / 2);
} catch (...) {
// Unexpected error
assert(false && "Unexpected exception");
}
assert(Obj::GetAliveObjectCount() == SIZE);
}
Obj::ResetCounters();
{
Vector<Obj> v(SIZE);
try {
v[SIZE - 1].throw_on_copy = true;
v.Reserve(SIZE * 2);
} catch (...) {
// Unexpected error
assert(false && "Unexpected exception");
}
assert(v.Capacity() == SIZE * 2);
assert(v.Size() == SIZE);
assert(Obj::GetAliveObjectCount() == SIZE);
}
}
void Test3() {
const size_t MEDIUM_SIZE = 100;
const size_t LARGE_SIZE = 250;
const int ID = 42;
{
Obj::ResetCounters();
Vector<int> v(MEDIUM_SIZE);
{
auto v_copy(std::move(v));
assert(v_copy.Size() == MEDIUM_SIZE);
assert(v_copy.Capacity() == MEDIUM_SIZE);
}
assert(Obj::GetAliveObjectCount() == 0);
}
{
Obj::ResetCounters();
{
Vector<Obj> v(MEDIUM_SIZE);
v[MEDIUM_SIZE / 2].id = ID;
assert(Obj::num_default_constructed == MEDIUM_SIZE);
Vector<Obj> moved_from_v(std::move(v));
assert(moved_from_v.Size() == MEDIUM_SIZE);
assert(moved_from_v[MEDIUM_SIZE / 2].id == ID);
}
assert(Obj::GetAliveObjectCount() == 0);
assert(Obj::num_moved == 0);
assert(Obj::num_copied == 0);
assert(Obj::num_default_constructed == MEDIUM_SIZE);
}
{
Obj::ResetCounters();
Vector<Obj> v_medium(MEDIUM_SIZE);
v_medium[MEDIUM_SIZE / 2].id = ID;
Vector<Obj> v_large(LARGE_SIZE);
v_large = v_medium;
assert(v_large.Size() == MEDIUM_SIZE);
assert(v_large.Capacity() == LARGE_SIZE);
assert(v_large[MEDIUM_SIZE / 2].id == ID);
assert(Obj::GetAliveObjectCount() == MEDIUM_SIZE + MEDIUM_SIZE);
}
{
Obj::ResetCounters();
Vector<Obj> v(MEDIUM_SIZE);
{
Vector<Obj> v_large(LARGE_SIZE);
v_large[LARGE_SIZE - 1].id = ID;
v = v_large;
assert(v.Size() == LARGE_SIZE);
assert(v_large.Capacity() == LARGE_SIZE);
assert(v_large[LARGE_SIZE - 1].id == ID);
assert(Obj::GetAliveObjectCount() == LARGE_SIZE + LARGE_SIZE);
}
assert(Obj::GetAliveObjectCount() == LARGE_SIZE);
}
assert(Obj::GetAliveObjectCount() == 0);
{
Obj::ResetCounters();
Vector<Obj> v(MEDIUM_SIZE);
v[MEDIUM_SIZE - 1].id = ID;
Vector<Obj> v_small(MEDIUM_SIZE / 2);
v_small.Reserve(MEDIUM_SIZE + 1);
const size_t num_copies = Obj::num_copied;
v_small = v;
assert(v_small.Size() == v.Size());
assert(v_small.Capacity() == MEDIUM_SIZE + 1);
v_small[MEDIUM_SIZE - 1].id = ID;
assert(Obj::num_copied - num_copies == MEDIUM_SIZE - (MEDIUM_SIZE / 2));
}
}
void Test4() {
const size_t ID = 42;
const size_t SIZE = 100'500;
{
Obj::ResetCounters();
Vector<Obj> v;
v.Resize(SIZE);
assert(v.Size() == SIZE);
assert(v.Capacity() == SIZE);
assert(Obj::num_default_constructed == SIZE);
}
assert(Obj::GetAliveObjectCount() == 0);
{
const size_t NEW_SIZE = 10'000;
Obj::ResetCounters();
Vector<Obj> v(SIZE);
v.Resize(NEW_SIZE);
assert(v.Size() == NEW_SIZE);
assert(v.Capacity() == SIZE);
assert(Obj::num_destroyed == SIZE - NEW_SIZE);
}
assert(Obj::GetAliveObjectCount() == 0);
{
Obj::ResetCounters();
Vector<Obj> v(SIZE);
Obj o{ID};
v.PushBack(o);
assert(v.Size() == SIZE + 1);
assert(v.Capacity() == SIZE * 2);
assert(v[SIZE].id == ID);
assert(Obj::num_default_constructed == SIZE);
assert(Obj::num_copied == 1);
assert(Obj::num_constructed_with_id == 1);
assert(Obj::num_moved == SIZE);
}
assert(Obj::GetAliveObjectCount() == 0);
{
Obj::ResetCounters();
Vector<Obj> v(SIZE);
v.PushBack(Obj{ID});
assert(v.Size() == SIZE + 1);
assert(v.Capacity() == SIZE * 2);
assert(v[SIZE].id == ID);
assert(Obj::num_default_constructed == SIZE);
assert(Obj::num_copied == 0);
assert(Obj::num_constructed_with_id == 1);
assert(Obj::num_moved == SIZE + 1);
}
{
Obj::ResetCounters();
Vector<Obj> v;
v.PushBack(Obj{ID});
v.PopBack();
assert(v.Size() == 0);
assert(v.Capacity() == 1);
assert(Obj::GetAliveObjectCount() == 0);
}
{
Vector<TestObj> v(1);
assert(v.Size() == v.Capacity());
// Операция PushBack существующего элемента вектора должна быть безопасна
// даже при реаллокации памяти
v.PushBack(v[0]);
assert(v[0].IsAlive());
assert(v[1].IsAlive());
}
{
Vector<TestObj> v(1);
assert(v.Size() == v.Capacity());
// Операция PushBack для перемещения существующего элемента вектора должна быть безопасна
// даже при реаллокации памяти
v.PushBack(std::move(v[0]));
assert(v[0].IsAlive());
assert(v[1].IsAlive());
}
}
int main() {
Test1();
Test2();
Test3();
Test4();
}