Skip to content

Instantly share code, notes, and snippets.

@stgatilov
Last active December 31, 2016 10:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stgatilov/75686a98ae28235688928e3c19b5b9a7 to your computer and use it in GitHub Desktop.
Save stgatilov/75686a98ae28235688928e3c19b5b9a7 to your computer and use it in GitHub Desktop.
Intrusive shared_ptr with weak_ptr (prototype)
#include <stddef.h>
#include <stdio.h>
#include <assert.h>
#include <algorithm>
#ifndef NO_CPP11
#include <type_traits>
#endif
// This is a prototype of intrusive SharedPtr + WeakPtr combo.
// It is not thread-safe yet, and it relies on rather unsafe things.
// Custom deleters are not supported.
//
// Any manageable object must inherit once from the RefCounted class.
// Two reference counters (strong/weak) are stored in there.
// Each smart pointer is just a single pointer.
// Object is destroyed when the last strong reference is gone.
// Memory block is deallocated when the last reference (strong or weak) is gone.
//
// Unlike std::shared_ptr + std::weak_ptr:
// 1. You can always init SharedPtr from raw pointer (given that object is not destroyed yet).
// 2. You can init WeakPtr from raw pointer (given that memory block is not deallocated yet).
// 3. You can check if WeakPtr is null (as for SharedPtr).
// 4. Less memory is used, both per object and per shared pointer.
// 5. You cannot get SharedPtr to a member of a managed object.
// 6. You cannot have SharedPtr to an object of non-modifiable existing class (e.g. SharedPtr<std::string>).
template<class T> class SharedPtr;
template<class T> class WeakPtr;
class RefCounters {
private:
template<class T> friend class PtrBase;
template<class T> friend class SharedPtr;
template<class T> friend class WeakPtr;
size_t strong_cnt;
size_t weak_cnt;
public:
RefCounters() : strong_cnt(0), weak_cnt(0) {}
};
class RefCounted : public RefCounters {
public:
virtual ~RefCounted() {}
};
template<class T> class PtrBase {
template<class S> friend class PtrBase;
template<class S> friend class SharedPtr;
template<class S> friend class WeakPtr;
#ifndef NO_CPP11
static_assert(std::is_base_of<RefCounted, T>::value, "SharedPtr and WeakPtr can only be used on objects inherited from RefCounted");
#endif
protected:
T *ptr;
RefCounters *Counter() const {
// Note: base class RefCounters should live even when T is dead.
// However, it seems that it is not guaranteed by C++ standard.
// See discussion here:
// http://stackoverflow.com/questions/41319114/use-member-of-primitive-type-after-object-destruction
RefCounters *base = ptr;
return base;
}
void DestroyObject() {
ptr->~T();
}
void DeallocateMemory() {
RefCounted *base = ptr;
operator delete(base);
}
#ifndef NO_CPP11
void Move(T *&src) {
ptr = src;
src = nullptr;
}
#endif
public:
T *Get() const { return ptr; }
T& operator* () const { return *ptr; }
T* operator-> () const { return ptr; }
operator bool () const { return ptr != NULL; }
bool operator! () const { return ptr == NULL; }
size_t GetUseCount() const {
return ptr ? Counter()->strong_cnt : 0;
}
size_t GetWeakCount() const {
return ptr ? Counter()->weak_cnt : 0;
}
void _debug_print() const {
printf("%p: %d + %d\n", ptr, (int)GetUseCount(), (int)GetWeakCount());
}
};
template<class T> class SharedPtr : public PtrBase<T> {
void Deinit() {
if (this->ptr) {
if (--this->Counter()->strong_cnt == 0) {
this->DestroyObject();
if (this->Counter()->weak_cnt == 0)
this->DeallocateMemory();
}
}
}
void Init(T *src) {
this->ptr = src;
if (this->ptr)
this->Counter()->strong_cnt++;
}
public:
~SharedPtr() {
this->Deinit();
}
SharedPtr() {
this->ptr = NULL;
}
explicit SharedPtr(T *src) {
this->Init(src);
}
explicit SharedPtr(const WeakPtr<T> &src) {
this->Init(src.ptr);
}
SharedPtr(const SharedPtr<T> &src) {
this->Init(src.ptr);
}
template<class S> SharedPtr(const SharedPtr<S> &src) {
this->Init(src.ptr);
}
void operator= (const SharedPtr<T> &src) {
this->Deinit();
this->Init(src.ptr);
}
template<class S> void operator= (const SharedPtr<S> &src) {
this->Deinit();
this->Move((T*&)src.ptr);
}
#ifndef NO_CPP11
SharedPtr(std::nullptr_t null) {
this->ptr = nullptr;
}
SharedPtr(SharedPtr<T> &&src) {
this->Move(src.ptr);
}
template<class S> SharedPtr(SharedPtr<S> &&src) {
this->Move(src.ptr);
}
void operator= (SharedPtr<T> &&src) {
this->Deinit();
this->Move(src.ptr);
}
template<class S> void operator= (SharedPtr<S> &&src) {
this->Deinit();
this->Move((T*&)src.ptr);
}
void operator= (std::nullptr_t null) {
this->Deinit();
this->ptr = nullptr;
}
#endif
void Swap(SharedPtr<T> &other) {
std::swap(this->ptr, other.ptr);
}
void Reset() {
this->Deinit();
this->ptr = NULL;
}
};
template<class T> class WeakPtr : public PtrBase<T> {
void Deinit() {
if (this->ptr) {
if (--this->Counter()->weak_cnt == 0)
if (this->Counter()->strong_cnt == 0)
this->DeallocateMemory();
}
}
void Init(T *src) {
this->ptr = src;
if (this->ptr)
this->Counter()->weak_cnt++;
}
public:
~WeakPtr() {
this->Deinit();
}
WeakPtr() {
this->ptr = NULL;
}
explicit WeakPtr(T *src) {
this->Init(src);
}
WeakPtr(const WeakPtr<T> &src) {
this->Init(src.ptr);
}
WeakPtr(const SharedPtr<T> &src) {
this->Init(src.ptr);
}
template<class S> WeakPtr(const SharedPtr<S> &src) {
this->Init(src.ptr);
}
template<class S> WeakPtr(const WeakPtr<S> &src) {
this->Init(src.ptr);
}
void operator= (const WeakPtr<T> &src) {
this->Deinit();
this->Init(src.ptr);
}
template<class S> void operator= (const SharedPtr<S> &src) {
this->Deinit();
this->Init(src.ptr);
}
template<class S> void operator= (const WeakPtr<S> &src) {
this->Deinit();
this->Init(src.ptr);
}
#ifndef NO_CPP11
WeakPtr(std::nullptr_t null) {
this->ptr = nullptr;
}
WeakPtr(WeakPtr<T> &&src) {
this->Move(src.ptr);
}
template<class S> WeakPtr(WeakPtr<S> &&src) {
this->Move((T*&)src.ptr);
}
void operator= (WeakPtr<T> &&src) {
this->Deinit();
this->Move(src.ptr);
}
template<class S> void operator= (WeakPtr<S> &&src) {
this->Deinit();
this->Move((T*&)src.ptr);
}
void operator= (std::nullptr_t null) {
this->Deinit();
this->ptr = nullptr;
}
#endif
void Swap(WeakPtr<T> &other) {
std::swap(this->ptr, other.ptr);
}
void Reset() {
this->Deinit();
this->ptr = NULL;
}
bool IsValid() const {
return this->ptr && this->GetUseCount() > 0;
}
};
//========================================================
// Trivial testing code
//========================================================
class Base : public RefCounted {
public:
virtual ~Base() {}
};
class Derived : public Base {
};
int main() {
WeakPtr<Base> w;
Derived *raw;
{
SharedPtr<Derived> pDer1(new Derived());
pDer1._debug_print();
w = pDer1;
w._debug_print();
raw = pDer1.Get();
}
w._debug_print();
WeakPtr<Derived> w2(raw);
w2._debug_print();
#ifndef NO_CPP11
WeakPtr<Base> w3 = std::move(w2);
#else
WeakPtr<Base> w3 = w2;
w2.Reset();
#endif
w2._debug_print();
w3._debug_print();
w.Reset();
w3._debug_print();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment