cpp/shared_pointer/main.cpp

601 lines
13 KiB
C++
Raw Permalink Normal View History

2023-07-16 07:03:47 +00:00
#include <set>
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
#include <cassert>
#include "smart_pointers.h"
//template<typename T>
//using SharedPtr = std::shared_ptr<T>;
//
//template<typename T>
//using WeakPtr = std::weak_ptr<T>;
//
//#define makeShared std::make_shared
//#define allocateShared std::allocate_shared
//
//template<typename T>
//using EnableSharedFromThis = std::enable_shared_from_this<T>;
struct Base {
virtual ~Base() {}
};
struct Derived: public Base {
static int constructed;
static int destructerd;
Derived() {
++constructed;
}
~Derived() {
++destructerd;
}
};
int Derived::constructed = 0;
int Derived::destructerd = 0;
void test_shared_ptr() {
using std::vector;
auto first_ptr = SharedPtr<vector<int>>(new vector<int>(1'000'000));
auto second_ptr = SharedPtr<vector<int>>(new vector<int>(10));
first_ptr.reset(new vector<int>());
second_ptr.reset();
SharedPtr<vector<int>>().swap(first_ptr);
assert(second_ptr.get() == nullptr);
assert(second_ptr.get() == nullptr);
for (int k = 0; k < 2; ++k) {
vector<SharedPtr<int>> ptrs;
for (int i = 0; i < 100'000; ++i) {
int* p = new int(rand() % 99'999);
ptrs.push_back(SharedPtr<int>(p));
}
std::sort(ptrs.begin(), ptrs.end(), [](auto&& x, auto&& y){return *x < *y;});
for (int i = 0; i + 1 < 100'000; ++i) {
assert(*(ptrs[i]) <= *(ptrs[i+1]));
}
while (!ptrs.empty()) {
ptrs.pop_back();
}
}
// test const
{
const SharedPtr<int> sp(new int(42));
assert(sp.use_count() == 1);
assert(*sp.get() == 42);
assert(*sp == 42);
}
}
struct Node;
struct Next {
SharedPtr<Node> shared;
WeakPtr<Node> weak;
Next(const SharedPtr<Node>& shared): shared(shared) {}
Next(const WeakPtr<Node>& weak): weak(weak) {}
};
struct Node {
static int constructed;
static int destructed;
int value;
Next next;
Node(int value): value(value), next(SharedPtr<Node>()) {
++constructed;
}
Node(int value, const SharedPtr<Node>& next): value(value), next(next) {
++constructed;
}
Node(int value, const WeakPtr<Node>& next): value(value), next(next) {
++constructed;
}
~Node() {
++destructed;
}
};
int Node::constructed = 0;
int Node::destructed = 0;
SharedPtr<Node> getCyclePtr(int cycleSize) {
SharedPtr<Node> head(new Node(0));
SharedPtr<Node> prev(head);
for (int i = 1; i < cycleSize; ++i) {
SharedPtr<Node> current(new Node(i));
prev->next.shared = current;
prev = current;
// std::cout << prev.use_count() << '\n';
}
//prev->next.shared.~SharedPtr<Node>();
//new (&prev->next.weak) WeakPtr<Node>(head);
prev->next.weak = head;
//prev->next.isLast = true;
return head;
}
int allocated = 0;
int deallocated = 0;
int allocate_called = 0;
int deallocate_called = 0;
int new_called = 0;
int delete_called = 0;
void test_weak_ptr() {
auto sp = SharedPtr<int>(new int(23));
WeakPtr<int> weak = sp;
{
auto shared = SharedPtr<int>(new int(42));
weak = shared;
assert(weak.use_count() == 1);
assert(!weak.expired());
}
assert(weak.use_count() == 0);
assert(weak.expired());
weak = sp;
auto wp = weak;
assert(weak.use_count() == 1);
assert(wp.use_count() == 1);
auto wwp = std::move(weak);
//assert(weak.use_count() == 0);
assert(wwp.use_count() == 1);
auto ssp = wwp.lock();
assert(sp.use_count() == 2);
sp = ssp;
ssp = sp;
assert(ssp.use_count() == 2);
sp = std::move(ssp);
assert(sp.use_count() == 1);
ssp.reset(); // nothing should happen
sp.reset();
unsigned int useless_value = 0;
for (int i = 0; i < 100'000; ++i) {
SharedPtr<Node> head = getCyclePtr(8);
SharedPtr<Node> nextHead = head->next.shared;
assert(nextHead.use_count() == 2);
useless_value += 19'937 * i * nextHead.use_count();
head.reset();
assert(nextHead.use_count() == 1);
}
assert(Node::constructed == 800'000);
assert(Node::destructed == 800'000);
// test inheritance
{
SharedPtr<Derived> dsp(new Derived());
SharedPtr<Base> bsp = dsp;
WeakPtr<Derived> wdsp = dsp;
WeakPtr<Base> wbsp = dsp;
WeakPtr<Base> wwbsp = wdsp;
assert(dsp.use_count() == 2);
bsp = std::move(dsp);
assert(bsp.use_count() == 1);
bsp.reset();
assert(wdsp.expired());
assert(wbsp.expired());
assert(wwbsp.expired());
}
assert(Derived::constructed == Derived::destructerd);
// test const
new_called = 0;
delete_called = 0;
{
SharedPtr<int> sp(new int(42));
const WeakPtr<int> wp(sp);
assert(!wp.expired());
auto ssp = wp.lock();
}
assert(new_called == 2);
assert(delete_called == 2);
}
struct NeitherDefaultNorCopyConstructible {
NeitherDefaultNorCopyConstructible() = delete;
NeitherDefaultNorCopyConstructible(const NeitherDefaultNorCopyConstructible&) = delete;
NeitherDefaultNorCopyConstructible& operator=(const NeitherDefaultNorCopyConstructible&) = delete;
NeitherDefaultNorCopyConstructible(NeitherDefaultNorCopyConstructible&&) = default;
NeitherDefaultNorCopyConstructible& operator=(NeitherDefaultNorCopyConstructible&&) = default;
explicit NeitherDefaultNorCopyConstructible(int x): x(x) {}
int x;
};
struct Accountant {
static int constructed;
static int destructed;
Accountant() {
++constructed;
}
Accountant(const Accountant&) {
++constructed;
}
~Accountant() {
++destructed;
}
};
int Accountant::constructed = 0;
int Accountant::destructed = 0;
void* operator new(size_t n) {
++new_called;
auto t = std::malloc(n);
return t;
}
void operator delete(void* ptr) {
++delete_called;
std::free(ptr);
}
struct VerySpecialType {};
void* operator new(size_t n, VerySpecialType) {
return std::malloc(n);
}
void operator delete(void* ptr, VerySpecialType) {
std::free(ptr);
}
template<typename T>
struct MyAllocator {
using value_type = T;
MyAllocator() = default;
template<typename U>
MyAllocator(const MyAllocator<U>&) {}
T* allocate(size_t n) {
++allocate_called;
allocated += n * sizeof(T);;
return (T*) ::operator new(n * sizeof(T), VerySpecialType());
}
void deallocate(T* p, size_t n) {
++deallocate_called;
deallocated += n * sizeof(T);
::operator delete((void*)p, VerySpecialType());
}
};
void test_make_allocate_shared() {
new_called = 0;
delete_called = 0;
{
auto sp = makeShared<NeitherDefaultNorCopyConstructible>(
NeitherDefaultNorCopyConstructible(0));
WeakPtr<NeitherDefaultNorCopyConstructible> wp = sp;
auto ssp = sp;
sp.reset();
assert(!wp.expired());
ssp.reset();
assert(wp.expired());
}
new_called = 0;
delete_called = 0;
{
auto sp = makeShared<Accountant>();
assert(Accountant::constructed == 1);
WeakPtr<Accountant> wp = sp;
auto ssp = sp;
sp.reset();
assert(Accountant::constructed == 1);
assert(Accountant::destructed == 0);
assert(!wp.expired());
ssp.reset();
assert(Accountant::destructed == 1);
Accountant::constructed = 0;
Accountant::destructed = 0;
}
new_called = 0;
delete_called = 0;
{
MyAllocator<NeitherDefaultNorCopyConstructible> alloc;
auto sp = allocateShared<NeitherDefaultNorCopyConstructible>(
alloc, NeitherDefaultNorCopyConstructible(0));
int count = allocated;
assert(allocated > 0);
assert(allocate_called == 1);
WeakPtr<NeitherDefaultNorCopyConstructible> wp = sp;
auto ssp = sp;
sp.reset();
assert(count == allocated);
assert(deallocated == 0);
assert(!wp.expired());
ssp.reset();
assert(count == allocated);
}
assert(allocated == deallocated);
assert(allocate_called == 1);
assert(deallocate_called == 1);
allocated = 0;
deallocated = 0;
allocate_called = 0;
deallocate_called = 0;
{
MyAllocator<Accountant> alloc;
auto sp = allocateShared<Accountant>(alloc);
int count = allocated;
assert(allocated > 0);
assert(allocate_called == 1);
assert(Accountant::constructed == 1);
WeakPtr<Accountant> wp = sp;
auto ssp = sp;
sp.reset();
assert(count == allocated);
assert(deallocated == 0);
assert(Accountant::constructed == 1);
assert(Accountant::destructed == 0);
assert(!wp.expired());
ssp.reset();
assert(count == allocated);
}
assert(allocated == deallocated);
assert(Accountant::constructed == 1);
assert(Accountant::destructed == 1);
assert(allocate_called == 1);
assert(deallocate_called == 1);
assert(new_called == 0);
assert(delete_called == 0);
}
//struct Enabled: public EnableSharedFromThis<Enabled> {
// SharedPtr<Enabled> get_shared() {
// return shared_from_this();
// }
//};
//void test_enable_shared_from_this() {
// {
// Enabled e;
// bool caught = false;
// try {
// e.get_shared();
// } catch (...) {
// caught = true;
// }
// assert(caught);
// }
//
// auto esp = makeShared<Enabled>();
//
// auto& e = *esp;
// auto sp = e.get_shared();
//
// assert(sp.use_count() == 2);
//
// esp.reset();
// assert(sp.use_count() == 1);
//
// sp.reset();
//}
struct A {
static int deleted;
virtual ~A() {
++deleted;
}
};
int A::deleted = 0;
struct A1: public A {
static int deleted;
virtual ~A1() {
++deleted;
}
};
int A1::deleted = 0;
struct A2: public A {
static int deleted;
virtual ~A2() {
++deleted;
}
};
int A2::deleted = 0;
struct A11: public A1 {
static int deleted;
virtual ~A11() {
++deleted;
}
};
int A11::deleted = 0;
struct A12: public A1 {
static int deleted;
virtual ~A12() {
++deleted;
}
};
int A12::deleted = 0;
struct A21: public A2 {
static int deleted;
virtual ~A21() {
++deleted;
}
};
int A21::deleted = 0;
struct A22: public A2 {
static int deleted;
virtual ~A22() {
++deleted;
}
};
int A22::deleted = 0;
int destroy_called;
template<typename T>
struct MyAlloc {
using value_type = T;
MyAlloc() = default;
template<typename U>
MyAlloc(const MyAlloc<U>&) {}
void destroy(T* p) {
++destroy_called;
p->~T();
}
T* allocate(size_t n) {
++allocate_called;
allocated += n * sizeof(T);;
return (T*) ::operator new(n * sizeof(T), VerySpecialType());
}
void deallocate(T* p, size_t n) {
++deallocate_called;
deallocated += n * sizeof(T);
::operator delete((void*)p, VerySpecialType());
}
};
void my_test_inheritance_destroy() {
{
SharedPtr<A22> a22 = allocateShared<A22, MyAlloc<A22>>(MyAlloc<A22>());
SharedPtr<A2> a2 = a22;
SharedPtr<A> a = a2;
assert(a.use_count() == 3);
a22.reset();
a.reset();
a = allocateShared<A21, MyAlloc<A21>>(MyAlloc<A21>());
WeakPtr<A> wa = a;
WeakPtr<A> wwa = wa;
a.reset(new A2());
}
assert(A22::deleted == 1);
assert(A21::deleted == 1);
}
void my_test_custom_deleter();
struct T {
~T() {
std::cout << "~" << std::endl;
}
};
int main() {
using namespace std;
std::allocator<T> all;
SharedPtr<T> p(all.allocate(1));
// p.reset();
// SharedPtr<int> p;
p.reset(new T);
p.reset(new T);
p.reset(new T);
p.reset(new T);
p.reset(new T);
p.reset();
std::cout << "alsdkfj" << endl;
// return 0;
static_assert(!std::is_base_of_v<std::shared_ptr<VerySpecialType>, SharedPtr<VerySpecialType>>,
"don't try to use std smart pointers");
// static_assert(!std::is_base_of_v<WeakPtr<VerySpecialType>, WeakPtr<VerySpecialType>>,
// "don't try to use std smart pointers");
//
test_shared_ptr();
std::cout << "1/5 passed" << std::endl;
test_weak_ptr();
std::cout << "2/5 passed" << std::endl;
test_make_allocate_shared();
std::cout << "3/5 passed" << std::endl;
std::cout << "starting my tests" << std::endl;
my_test_inheritance_destroy();
std::cout << "4/5 passed" << std::endl;
my_test_custom_deleter();
std::cout << "5/5 passed" << std::endl;
std::cout << "now it should work" << std::endl;
// test_enable_shared_from_this();
}
template<typename T>
struct MyDeletor {
static int deleted;
void operator()(T* x) {
::delete x;
++deleted;
}
};
template<typename T>
int MyDeletor<T>::deleted = 0;
void my_test_custom_deleter() {
{
SharedPtr<Accountant> p(new Accountant(), MyDeletor<Accountant>());
SharedPtr<Accountant> pp = std::move(p);
std::cout << pp.use_count() << std::endl;
SharedPtr<Accountant> h = pp;
std::cout << h.use_count() << std::endl;
// p = allocateShared<Accountant, MyAlloc<Accountant>>(MyAlloc<Accountant>());
}
std::cout << delete_called << std::endl;
assert(delete_called == 4);
assert(destroy_called == 3);
destroy_called = 0;
{
auto* smth = new Accountant();
SharedPtr<Accountant> p(smth, MyDeletor<Accountant>(), MyAlloc<Accountant>());
SharedPtr<Accountant> pp = std::move(p);
SharedPtr<Accountant> h = pp;
p = allocateShared<Accountant, MyAlloc<Accountant>>(MyAlloc<Accountant>());
}
assert(delete_called == 5);
assert(destroy_called == 1);
}