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?

  • @LokiAstari Care to elaborate? Otherwise ill just assume your a troll and ignore your answer/have it deleted :). – user240713 Aug 13 '15 at 10:28

You should probably try to understand the reasoning behind std::weak_ptr::lock. After you get the pointer you need to be sure that it won't get deleted by another thread while you are using it. That's why it returns a shared_ptr to let the calling code decide when the object is allowed to be deleted.

if(auto ptr = flimsy.Get())
{
    // set and alive, 
    //  however ptr cannot be used because another thread can still delete the owning SharedPtr at any point.
}

If you only need to check alive status then I suggest using std::weak_ptr::expired().

if(!weak.expired())
{
    // set and alive. no ptr to work with
}

If you are in a single threaded environment then your implementation is okay. But if you ensure the shared_ptr is cleaned up before you return then shared_ptr's life won't be extended.

if(auto ptr = weak.lock())
{
    // set and alive, 
    // ptr can be used 

    //at the end of the block ptr will be cleaned up and won't prevent cleanup
}
  • Yah i don't need it to support multi-threading. Should have mentioned that. There's only one instance where it lives and there are guarantees for it to be killed without anyone else to be using it. So using shared/weak from std is overkill for me and provides behavior i don't want, that is lock() from weak extending the lifetime of shared_ptr. My flimsy pointer prevents it from extending that lifetime when it shouldn't, it's solely to check if it is alive. I also need to check if it is null, i could use something like boost::optional though instead. There are some things though, like what – user240713 Aug 11 '15 at 19:56
  • happens when you do flimsy = SharedPtr(). – user240713 Aug 11 '15 at 20:01

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.