namespace Impl
{
template<typename T>
class SharedPtrCounter
{
public:
SharedPtrCounter(T* e) : element(e)
{
}
void IncrementFlimsyCounter()
{
++flimsyCounter;
}
void DecrementFlimsyCounter()
{
if(--flimsyCounter == 0 && !element)
{
delete this;
}
}
void Kill()
{
element = nullptr;
if(flimsyCounter == 0)
{
delete this;
}
}
int Count() const
{
return flimsyCounter;
}
T* Element() const
{
return element;
}
private:
int flimsyCounter = 0;
T* element;
};
}
template<typename> class FlimsyPtr;
template<typename T>
class SharedPtr // limited implementation of actual shared pointer (only 1 instance allowed, can be promoted though)
{
friend class FlimsyPtr<T>;
public:
using ElementType = T;
template<typename... Args>
static SharedPtr Make(Args && ...args) { return SharedPtr(new T(std::forward<Args>(args)...)); }
SharedPtr() : element(nullptr), counter(nullptr)
{
}
~SharedPtr()
{
if(counter)
{
counter->Kill();
}
}
SharedPtr(const SharedPtr&) = delete;
SharedPtr& operator = (const SharedPtr&) = delete;
SharedPtr(SharedPtr&& shared)
{
element = shared.element;
counter = shared.counter;
shared.element = nullptr;
shared.counter = nullptr;
}
void Reset()
{
if(counter)
{
counter->Kill();
element = nullptr;
counter = nullptr;
}
}
int Count() const
{
return counter ? counter->Count() : 0;
}
ElementType* Get() const
{
return element;
}
ElementType* operator -> ()
{
return element;
}
private:
SharedPtr(ElementType* e) : element(e), counter(new Impl::SharedPtrCounter<T>(e))
{
}
ElementType* element;
Impl::SharedPtrCounter<T>* counter;
};
template<typename T>
class FlimsyPtr
{
public:
using ElementType = T;
FlimsyPtr() : counter(nullptr)
{
}
FlimsyPtr(const FlimsyPtr& flimsy)
{
if(counter = flimsy.counter)
{
counter->IncrementFlimsyCounter();
}
}
FlimsyPtr(FlimsyPtr&& flimsy)
{
counter = flimsy.counter;
flimsy.counter = nullptr;
}
FlimsyPtr& operator = (const FlimsyPtr& flimsy)
{
if(counter != flimsy.counter)
{
if(counter)
{
counter->DecrementFlimsyCounter();
}
if(counter = flimsy.counter)
{
counter->IncrementFlimsyCounter();
}
}
return *this;
}
FlimsyPtr& operator = (FlimsyPtr&& flimsy)
{
if(counter)
{
counter->DecrementFlimsyCounter();
}
counter = flimsy.counter;
flimsy.counter = nullptr;
return *this;
}
FlimsyPtr(const SharedPtr<ElementType>& shared)
{
if(counter = shared.counter)
{
counter->IncrementFlimsyCounter();
}
}
~FlimsyPtr()
{
if(counter)
{
counter->DecrementFlimsyCounter();
}
}
bool IsAlive()
{
return Get() != nullptr;
}
ElementType* Get() const
{
return counter ? counter->Element() : nullptr;
}
explicit operator bool const ()
{
return counter != nullptr;
}
bool operator == (const FlimsyPtr& rhs)
{
return counter == rhs.counter;
}
bool operator == (std::nullptr_t)
{
return counter == nullptr;
}
int Count() const
{
return counter->Count();
}
private:
Impl::SharedPtrCounter<T>* counter;
};
The point is to allow 3 control paths:
SharedPtr<int> shared = SharedPtr<int>::Make(0);
FlimsyPtr<int> flimsy = shared; // set, alive example
FlimsyPtr<int> flimsy0 = SharedPtr<int>::Make(0); // set, dead example
FlimsyPtr<int> flimsy1; // not set example
if(flimsy)
{
if(auto ptr = flimsy.Get())
{
// set and alive, ptr can be used
}
else
{
// set, dead
}
else
{
// not set, do other things
}
I'm going to use it where there should only be one instance of SharedPtr
and I only really need FlimsyPtr
to check if it is alive or not when I try and use it (and whether it was set a value). std::weak_ptr
's lock()
therefore has behavior I don't want, as I only want once instance to exist. Using that implementation could extend the lifetime of the object more than I'd want. Really simple implementation, no casts or anything yet. Any opinions?