Skip to main content
gamedev.net
🔒 Locked

smart pointer problem

Started by penwan Aug 23, 2006 at 10:10 PM 10 replies 1.6k views
Original Post
penwan
penwan
I'm trying to write an intrusive smart pointer for use in my game. All classes that are intended to be dealt with by smart pointers derive from the class "Object" The problem I'm having is illustrated in this simple snippet:
#include "xiCore/Object.h"
#include <cstdio>

class Sample : public xi::Object
{
    int blah;
};

typedef xi::Pointer<Sample> SamplePtr;

void test(xi::ObjectPtr s)
{
}

int main(void)
{
    SamplePtr sample = new Sample();
    xi::ObjectPtr object = sample;
    object = 0;
    object = sample;

    test(sample);

    return 0;
}
The line test(sample) results in a compiler error "error C2664: 'test' : cannot convert parameter 1 from 'SamplePtr' to 'xi::ObjectPtr'" But if I comment out that line the rest of the program works fine. That is, I can construct an ObjectPtr from a SamplePtr and I can set an ObjectPtr equal to a SamplePtr. But passing it in to a function does not work. Here are the header files for the classes:
#ifndef XI_POINTER_H
#define XI_POINTER_H

namespace xi
{
    // A smart pointer. Type T should subclass Object.
    template <typename T>
    class Pointer
    {
    public:
        Pointer(T* p = 0) : mPtr(p) 
        {
            if(mPtr != 0)
                mPtr->addRef();
        }

        Pointer(const Pointer<T>& cp)
        {
            Pointer<T>& p = const_cast<Pointer<T>&>(cp);
            mPtr = p.get();

            if(mPtr != 0)
                mPtr->addRef();
        }

        template <typename T2>
        Pointer(const Pointer<T2>& cp)
        {
            Pointer<T2>& p = const_cast<Pointer<T2>&>(cp);
            mPtr = reinterpret_cast<T*>(p.get());

            if(mPtr != 0)
                mPtr->addRef();
        }

        ~Pointer(void)
        {
            if(mPtr != 0)
                mPtr->removeRef();
        }

        bool valid(void) const
        {
            return (mPtr != 0);
        }

        Pointer<T>& operator=(T* p)
        {
            reset(p);

            return *this;
        }

        Pointer<T>& operator=(Pointer<T>& p)
        {
            reset(p.get());

            return *this;
        }

        template <typename T2>
        Pointer<T>& operator=(const Pointer<T2>& cp)
        {
            Pointer<T2>& p = const_cast<Pointer<T2>&>(cp);
            reset(p.get());

            return *this;
        }

        template <typename T2>
        Pointer<T>& operator=(T2* p)
        {
            reset(p);
            return *this;
        }

        T& operator*(void) const
        {
            return *mPtr;
        }

        T* operator->(void) const
        {
            return mPtr;
        }

        T* get(void) const
        {
            return mPtr;
        }

        //operator T*(void) const
        //{
        //    return mPtr;
        //}

        void release(void)
        {
            if(mPtr != 0)
                mPtr->removeRef();

            mPtr = 0;
        }

        void reset(T* p = 0)
        {
            if(mPtr != 0)
                mPtr->removeRef();

            mPtr = p;

            if(mPtr != 0)
                mPtr->addRef();
        }

        template <typename T2>
        operator Pointer<T2>(void)
        {
            return Pointer<T2>(*this);
        }

    private:
        T* mPtr;
    };
}

#endif
#ifndef XI_OBJECT_H
#define XI_OBJECT_H

#include "Common.h"
#include "Pointer.h"

namespace xi
{
    // This is the root class for all objects in the engine. For now it just 
    // provides reference counting for smart pointers. In the future, it can be
    // used for object tracking, memory tracking, RTTI, etc. In general, objects
    // should be managed with the Pointer smart pointer.
    class Object
    {
    public:
        virtual ~Object(void) = 0;

        void addRef(void) {++mRefCount;}

        void removeRef(void)
        {
            if (--mRefCount <= 0)
                delete this;
        }

    protected:
        Object(void) : mRefCount(0) {}

    private:
        s32 mRefCount;
    };

    inline Object::~Object(void) {}

    typedef Pointer<Object> ObjectPtr;
}

#endif
I have a templatized copy constructor, assignment operator, and conversion operator...one of which I should think would handle this situation. Any idea what's wrong?
penwan
penwan
Because auto_ptr does not use reference counting. If I tried to pass an auto_ptr around ownership of the raw pointer passes with it and the original pointer is invalidated.

As for boost, I don't really want to go to all that hassle just to have a reference counting pointer. The one I have works perfectly, except in this one specific instance. When I solve this, I'll be done with it.

And as for reinventing the wheel, it took me all of 5 minutes to throw together that pointer class.

Now...do you have any idea on what I did wrong? ;p
jflanglois
jflanglois
Quote:
Original post by Anonymous Poster
Why not use std::auto_ptr? Its a lot better then yours and does what you want. If you want weak pointers, use boost. Don't waste your time reinventing simple wheels.


Although I agree that the OP should be using a good third party smart pointer implementation, do note that his Pointer class is not equivalent to auto_ptr, it is a reference counted pointer, as opposed to a "RAII adapter". OP: if this is not for learning, then please do use boost::shared_ptr, boost::weak_ptr, and friends [edit: As snk_kid points out further, in this case you would be using intrusive_ptr. Sorry for not being clear. ]. Believe it or not, they will be part of the standard library (they are currently in tr1).

As for the problem you are having, the answer is quite simple: Pointer< Sample > does not derive Pointer< Object >, even though Sample derives from Object. The way to solve this is to "fake" the derived object assignment of raw pointers with a template copy constructor in Pointer:

template< class OtherType >
Pointer( Pointer< OtherType > & p ) : mPtr( p.mPtr ) {
// Adjust reference count
}



jfl.

[Edited by - jflanglois on August 24, 2006 2:36:14 PM]
penwan
penwan
jfl:

I have already defined that function as:

    template <typename T2>        Pointer(const Pointer<T2>& cp)        {            Pointer<T2>& p = const_cast<Pointer<T2>&>(cp);            mPtr = reinterpret_cast<T*>(p.get());            if(mPtr != 0)                mPtr->addRef();        }

It still does not compile.
jflanglois
jflanglois
Ah, you are right. The problem is not there, it is because you have defined an implicit conversion function. Remove operator Pointer< T2 > from the Pointer class.
penwan
penwan
Yep, that did the trick -- thanks!
Enigma
Enigma
Quote:
Scott Meyers, Effective STL, Item 7
The STL itself contains no reference-counting smart pointer, and writing a good one - one that works correctly all the time - is tricky enough that you don't want to do it unless you have to. I published the code for a reference-counting smart pointer in More Effective C++ in 1996, and despite basing it on established smart pointer implementations and submiting it to extensive pre-publication reviewing by experienced developers, a small parade of valid bug reports has trickled in for years. The number of subtle ways in which reference-counting smart pointers can fail is remarkable.

Use Boost.Smart_Ptr. Here, I even used bcp to extract just that part of the library and uploaded it for you (506kb).

Σnigma
snk_kid
snk_kid
Quote:
Original post by jflanglois
if this is not for learning, then please do use boost::shared_ptr, boost::weak_ptr, and friends. Believe it or not, they will be part of the standard library (they are currently in tr1).


In this case for what he wants is boost::intrusive_ptr, since his objects are going to maintain an embedded reference count it is more efficient than using boost::shared_ptr but boost::intrusive_ptr is an intrusive solution thus less general and hence the name. Also note that boost::intrusive_ptr is not being reviewed in TR1 and has not been proposed for standardization.
penwan
penwan
Thanks, Enigma -- very cool of you.

snk_kid: yes, I'm interested in the intrusive_ptr. I'll likely extract just that from the download Enigma provided and use that in my project.

As for why I did it myself, it was simply as a learning experience and because it seemed rather easy. Looking at boost::intrusive_ptr, my version is nearly identical, though it looks like there are a few ways I could clean up mine.
jflanglois
jflanglois
Quote:
Original post by snk_kid
Quote:
Original post by jflanglois
if this is not for learning, then please do use boost::shared_ptr, boost::weak_ptr, and friends. Believe it or not, they will be part of the standard library (they are currently in tr1).


In this case for what he wants is boost::intrusive_ptr, since his objects are going to maintain an embedded reference count it is more efficient than using boost::shared_ptr but boost::intrusive_ptr is an intrusive solution thus less general and hence the name.

Yes, I was aware of that. I was suggesting that he use the Boost smart pointers in general.

Quote:
Also note that boost::intrusive_ptr is not being reviewed in TR1 and has not been proposed for standardization.

That I was not aware of. :)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.