Last active
December 31, 2016 10:56
-
-
Save stgatilov/75686a98ae28235688928e3c19b5b9a7 to your computer and use it in GitHub Desktop.
Intrusive shared_ptr with weak_ptr (prototype)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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