diff --git a/Makefile b/Makefile index b0ca37f..25a975e 100644 --- a/Makefile +++ b/Makefile @@ -7,4 +7,4 @@ clean: undo: undo.cpp @mkdir -p bin - clang++ undo.cpp -o bin/undo --std=c++14 + clang++ undo.cpp -o bin/undo --std=c++14 -O0 -g -fsanitize=address,undefined diff --git a/shared.h b/shared.h new file mode 100644 index 0000000..5511c1a --- /dev/null +++ b/shared.h @@ -0,0 +1,118 @@ +#ifndef SHARED_H +#define SHARED_H + +#include +#include +#include +#include + +namespace sandbox { + +#if DEBUG_RETAIN +static int cb_count = 0; +#endif + +template +struct shared_ptr { + struct control_block { + template + control_block(U *u_, size_t c_) : t(u_), strong_count(u_) + { +#if DEBUG_RETAIN + printf("CB #%d ctor\n", id = ++cb_count); +#endif + } + + control_block(T *t_, size_t c_) : t(t_), strong_count(c_) { +#if DEBUG_RETAIN + printf("CB #%d ctor\n", id = ++cb_count); +#endif + } + + ~control_block() { +#if DEBUG_RETAIN + printf("CB #%d dtor\n", id = cb_count--); +#endif + } + +#if DEBUG_RETAIN + int id; +#endif + T *t; + size_t strong_count; + }; + + template explicit shared_ptr(U *u) + : cb(new control_block(static_cast(u), 0)) { + retain(cb); + } + + shared_ptr(const shared_ptr &rhs) : cb(rhs.cb) { + retain(cb); + } + + template + shared_ptr(shared_ptr &&rhs) { + // FIXME: this seems like an ugly hack / potential UB. + cb = reinterpret_cast(rhs.cb); + retain(cb); + } + + shared_ptr &operator=(const shared_ptr &rhs) { + if (this != &rhs) { + release(cb); + cb = rhs.cb; + retain(cb); + } + return *this; + } + + ~shared_ptr() { + release(cb); + } + + void release(control_block *&cb) { +#if DEBUG_RETAIN + printf("release %d --> %zu\n", cb->id, cb->strong_count); +#endif + + if (--cb->strong_count) + return; + + assert(cb->strong_count == 0 && "releasing, but there are still referers"); + + delete cb->t; + cb->t = nullptr; + + delete cb; + cb = nullptr; + } + + void retain(control_block *cb) { + cb->strong_count++; +#if DEBUG_RETAIN + printf("retain %d --> %zu\n", cb->id, cb->strong_count); +#endif + } + + T *operator->() { + assert(cb && cb->strong_count && "dereferencing without a pointee"); + return cb->t; + } + + const T *operator->() const { + assert(cb && cb->strong_count && "dereferencing without a pointee"); + return cb->t; + } + + control_block *cb; +}; + +template +shared_ptr make_shared(Args... args) { + return shared_ptr(new T(std::forward(args)...)); +} + +} + +#endif \ No newline at end of file diff --git a/undo.h b/undo.h index 281afa8..34542ec 100644 --- a/undo.h +++ b/undo.h @@ -6,6 +6,8 @@ * https://www.youtube.com/watch?v=bIhUE5uUFOA */ +#include "shared.h" + #include #include #include @@ -17,7 +19,7 @@ void draw(const T &t, std::ostream &out, size_t position); class object_t { public: template - object_t(T x) : self_(std::make_shared>(std::move(x))) {} + object_t(T x) : self_(sandbox::make_shared>(std::move(x))) {} friend void draw(const object_t &x, std::ostream &out, size_t position) { x.self_->draw_(out, position); @@ -39,7 +41,7 @@ class object_t { T data_; }; - std::shared_ptr self_; + sandbox::shared_ptr self_; }; using document_t = std::vector;