Advertisement

Weird C++ type conversion problem (HARD)...

Started by September 03, 2000 03:08 PM
74 comments, last by Dire.Wolf 24 years, 3 months ago
I have a couple ideas that I''m working on and a few of you here inspired me to work on a few new approaches. I''ll keep you guys informed.

Thanks again for the help.

Dire Wolf
direwolf@digitalfiends.com
[email=direwolf@digitalfiends.com]Dire Wolf[/email]
www.digitalfiends.com
vladg:

Mine worked for upcasting, and it only involved adding one function to the original poster''s code...why are conversion operators needed here?

BTW if you want to, take a look at the auto_ptr class in the STL docs. The class definition is about 10-20 lines (very small, actually) and most of the functions are very simple. It doesn''t do reference counting, but it''ll give you an idea of what things you need to get the syntax you like.

VC doesn''t seem to be able to handle this:


class surface
{
public:
operator LPDIRECTDRAWSURFACE () { return data; }

private:
LPDIRECTDRAWSURFACE data;
};

surface mysurface; // already have all the construction in place
mysurface->Blt(...); // Blt isn''t a member of surface...

// also, it doesn''t work if I add a -> operator...



I had a terrible time trying to get it to work, and in the end I just encapsulated what I needed to without exposing the LPDIRECTDRAWSURFACE pointer.


- null_pointer
Sabre Multimedia
Advertisement
To null_pointer:
You are right, templated constructor and operator =() are much better. What I posted here first is pretty much junk - the Handle template does not need a second template parameter. I''ll delete it.

quote:
I had a terrible time trying to get it to work, and in the end I just encapsulated what I needed to without exposing the LPDIRECTDRAWSURFACE pointer.

Odd. If you are still intersted in this you can compare your code to smart pointer templates for COM interfaces: CComPtr, _com_ptr_t, etc. I believe all of them implement a cast to "plain" interface pointer type.

Anyway, here''s my latest version:
    // the only reason for this class is to make m_pcount protectedclass HandleBase{public:    HandleBase() : m_pcount(new long(1)) {}    HandleBase(const HandleBase& r) : m_pcount(r.m_pcount) {}    virtual ~HandleBase() = 0 {} // this class cannot be instantiated    HandleBase& operator =(const HandleBase& r)    {        m_pcount = r.m_pcount;        return *this;    }protected:    long*   m_pcount;};template<class T>class Handle : public HandleBase{public:    explicit Handle(T* p) : HandleBase(), m_ptr(p) {}    // templated constructor handles conversion between Handle<Foo> and Handle<Bar>    template<class T2> Handle(const Handle<T2>& r) : HandleBase(r),        m_ptr(static_cast<T*>((T2*)r)) { (void) AddRef(); }    virtual ~Handle() { (void) Release(); }    // templated operator = also handles conversion between Handle<Foo> and Handle<Bar>    template<class T2> Handle<T>& operator =(const Handle<T2>& r)    {        if(m_ptr != static_cast<T*>((T2*)r))        {            (void) Release();            HandleBase::operator =(r);            m_ptr = static_cast<T*>((T2*)r);            AddRef();        }        return *this;    }    T* operator ->() const { return m_ptr; }    operator T* () const { return m_ptr; }    long AddRef()    {         if(m_ptr && m_pcount)            return ++(*m_pcount);        else            throw "AddRef() error";    }    long Release()    {         if(m_ptr && m_pcount)        {            long refcount = --(*m_pcount);            if(0 == refcount)            {                delete m_pcount;                m_pcount = NULL;                delete m_ptr;                m_ptr = NULL;            }            return refcount;        }        else            throw "Release() error";    }protected:    T*      m_ptr;};typedef Handle<Foo> FooHandle;    
My suggestion? Here's a much simpler generic handle type

typedef unsigned int my_handle;


Seriously, that has to be one of the most perverse uses of C++-for-the-sake-of-C++ I've ever seen. There just can't be a legitimate reason to use something that complicated. If you want to save yourself countless debugging headaches and compiler incompatibilites, tone it down =o

Edited by - daveb on September 5, 2000 2:42:45 AM
Volition, Inc.
quote: Original post by daveb

My suggestion? Here''s a much simpler generic handle type

typedef unsigned int my_handle;


I think you are missing the point. This is not a handle as in HWND, it''s a reference counted pointer to a class instance aka smart pointer. Refcounting + smart pointers = good thing(tm). I''ve been using smart pointers to COM interfaces for 2+ years and they really help to prevent most of the interface leaks.

quote:
Seriously, that has to be one of the most perverse uses of C++-for-the-sake-of-C++ I''ve ever seen.

Ah, perversions... that''s what women love us for... or so I belive

quote:
There just can''t be a legitimate reason to use something that complicated. If you want to save yourself countless debugging headaches and compiler incompatibilites, tone it down =o

You only need to write and debug smart pointer once. That''s one day, two at most. Sometimes you waste as much time debugging a single memory leak. On a reasonably large project you''d probably see several weeks spent on chasing down hard-to-find leaks. Do the math.
Besides, doing stuff like this once in a while is good for programmer''s soul
I would have to strongly disagree. When working on a large-scale game project (or even a medium scale one), this sort of super-C++ mentality can really burn you. Simplicity is the key. You throw this class in front of a 1 or 2 year guy with little exprerience and his eyes are going to cross. Keeping simple, readable, and _standard_ code is extremely crucial.

Not to mention the fact that this class does what is essentially random dynamic memory allocation (say goodbye to console portability). A good # of C++ compilers are not going to get the code right. And debugging with templates can be a major pain.

Now, I'm not trying to flame C++ here (not too much, anyway). I'm just saying, heavy C++ is out of place in game programming simply because of the inherent complexity.

I guess what I'm really saying, is that I see way too many young programmers going down the wrong path and really ruining themselves because they've been convinced C++ is the right way. Where I work, we've rejected _lots_ of potential programmers simply because they can't see outside of the C++ box. Its really terrible. They don't understand the significance of memory allocation, the significance of easy to read code, or the significance of _few_ layers of abstraction. In my experience, everyone _thinks_ they known how to use C++, but almost no one really really does. I would just love to see a shift in the mentality from C++-is-king to C-is-king-with-some-C++-on-the-side. The kinds of C++ delusions I see flying around these boards is downright frightening.

Edited by - daveb on September 5, 2000 10:58:17 AM
Volition, Inc.
Advertisement
daveb:

Have you been listening to anything anyone has said? Or are you too wrapped up in your crusade to think straight? I have heard enough of this attitude...

1. The class wasn't made to dynamic memory allocation - it controls deallocation. If you'll look at the constructor, you'll notice that it takes a pointer to existing memory. I don't work with consoles, but I don't see how console portability goes down the tubes with the Handle class - can't consoles handle pointers?

The Handle class seems fine for portability, since any code that needs to be changed can be changed _transparently_ inside the Handle class constructor.

2. Making a handle class is a great way to track memory usage, reference-counting, etc. By encapsulating it into a class, you can log instances, and accesses, and quite a bit more VERY EASILY. All you have to do is change the code in the Handle class, instead of making changes all over the code. For the clueless, that is THE point of making classes - controlling and specifying which code can be accessed in what way from other code - in one word, it's DESIGN. You need to think about the big picture for a second. Part of making good code is making it extendible for what you need. Only the original poster really knows what he needs the software to do - why not ask him instead of declaring that you are right?

3. C programmers are so _pathetically_ defensive of their language that they imagine lots of little attacks coming from all over the place. They also become so narrow-minded that they miss out on a lot of great tools. If you don't understand something, don't knock it. Learn it.

4. Do you really know how to use C++?


- null_pointer
Sabre Multimedia


Edited by - null_pointer on September 5, 2000 6:47:24 PM
I'm just curious, why not create a common, reference-counted base class for these type of objects? I know it's not easy if you don't own all the code, but it seems like it would be much easier to use vs. this type of template class.

ie:
        class Base{public:  void addRef() { ++m_refCount; }  void release(); // if (--m_refCount <= 0) delete this;  Base(): m_refCount(1) {}protected:  virtual ~Base(); // prevent 'delete' calls instead of release.protected:  int m_refCount;}        


Anyway, using a template means you have to 'new' you refCount variable, which (I would think) would cause increased memory fragmentation after a while.

Plus the template code is generated per class, as opposed to once for a base class.

Once again, I know the need for a templatized,RC class. I'm just making sure he's not doing something that is a little more difficult for a good reason.

If you're worried about memory leaks, make a template pointer class for objects derived specifically from 'Base'. It should cut down on the template's per-class overhead.



Edited by - Freeside on September 5, 2000 7:13:03 PM
(KABOOM)

I agree with null_pointer, if anything C++ enhances portability AND readability. Now that the C++ standard has been finalized, compiler writers have no choice but to conform or be labelled as "non-standard". C went through the exact same situation as C++ is going through now (and don''t pretend it didn''t).

The power of C++ is that it doesn''t HAVE to be used as an OO language. You can use it as an enhanced C with the same portability you''ve come to know and love.

C++ really shines when you begin to take advantage of its OO mechanisms (generic programming and polymorphism). Throw in stronger type checking, better optimizations, less dependancy on pre-compiler macros, and you have a powerful language indeed.

What I find amusing is that you belittle a language that you do not fully understand.

- Dire Wolf
direwolf@digitalfiends.com

[email=direwolf@digitalfiends.com]Dire Wolf[/email]
www.digitalfiends.com
Freeside,

I believe you are forgetting that each class derived from the Base class has a vtable and each object has a vptr (and hence per class/object data

What I do know is that what you have suggested is called 'intrusive' reference counting, meaning, all classes that need to implement reference counting must derive from a common Base class.

What I am looking for is a non-intrusive approach to reference counting.

NOTE: Something I do want to mention is that intrusive reference counting is typically faster than non-intrusive reference counting

- Dire Wolf
direwolf@digitalfiends.com


Edited by - Dire.Wolf on September 5, 2000 7:24:06 PM

Edited by - Dire.Wolf on September 5, 2000 8:02:07 PM
[email=direwolf@digitalfiends.com]Dire Wolf[/email]
www.digitalfiends.com

This topic is closed to new replies.

Advertisement