Handles vs. SmartPointers - the discussion ;-)

Started by
18 comments, last by VizOne 20 years, 2 months ago
I''ve used Handles to good effect for everything from vertex buffers to matrices. Probably the worst thing about them is that you CAN fire and forget with them, and have the system delete everything at the end. It is possible to end up with a massive amount of unused, and unfreed memory running around.

Z.
______________"Evil is Loud"
Advertisement
I agree about the unused resources. The way my current engine is set up (and projects), I am only worried about scenes with a set number of possible resources, which I load up at the start. So resource management is not a problem. For a game like Dungeon Seiege, I would just add a reference counter (much like COM). Even then, the managers could easily be configured to keep the resource around for a set time period to act as a sort of cache. It is because of this functionality that I wanted them to be seperate from their container, which in this thread is called CRenderer.
quote:Original post by Taulin
In fact, calling that static accessor may actually be more intensive, if not equal, but again, performance is not the issue here, I do not want to dwell on it.

As the accessor is inlined it''s up to the compiler to make it fast...

quote:
I would consider seeing the pointer in a function''s prototype/statement better design simply because I know what needs the CRender by simply looking at the header files.
[/quote
Well, that one''s yours!


Could you explain why you think using a globally acceesable reference is better than passing a pointer explicitly?

I see two reasons (well, it were three but I forgot one *g*):
1) You have to store the pointer to the instance somewhere. Well, that''s not actually a problem, but I like singletons to carry their instance themselves.
2) I like to assert pointers in my debug builds, and with the static accessor I can to that at one centralized place (i.e. the accessor function itself) and don''t have to do it in every function that uses it.

quote:Original post by Void
There is really no need for a smart pointer here, is there? What safety does the smart pointer provide over the raw pointer, since all it contains is a reference to some memory managed by the renderer?

Again, mainly two reasons:
1) A pointer may be tried to deleted *ouch*, that would be bad, wouldn''t it? The object the smart pointer is attached to can''t be deleted directly through the smart pointer (of course, it''s deleted when no sp points to it anymore)
2) More important: Objects stay valid even if they aren''t managed by the renderer anymore: let''s say you have a meshobject with a texture the meshobject itself is managed by the renderer, and the texture within it, too (so the texture may be shared among several mesh-objects). All the lists of managed objects in the renderer are smart ptrs as well. Now you (some kind of) flush the texture list of the renderer. This will delete all textures, that are not longer used. But all textures that *are* used (i.e. for which the refcount is >0) are still valid. And that''s what I want.

quote:
What you want is returning a proxy object, which doesn''t require a smart pointer.

I didn''t understand that exactly. Could you explain?

quote:
I suggest reading "Modern C++ Design" for a understanding (and good implementation) of smart pointers.

Is my implementation that bad Besides I think I got the point of smart pointers :D Nevertheless the book might be worth a look at.

Bye, VizOne
Andre Loker | Personal blog on .NET
Even the smart pointer approach isn''t very OO.

As you can''t extend any of the classes unless you can modify the CRenderer class to add in extra:

CRenderer::CreateMesh(X)Object functions etc.

A better way IMO is to just have an AddMesh, which takes into it a pointer to a base class Mesh. Then let the calling "Module" be responsible for memory management.

This doesn''t prevent you from reusing a resource it just means that when you do your final remove: The calling module is told it''s reference count is 0, and therefore CAN/SHOULD be deleted.

That''s offcourse if you want it to be OO, the way you have it, it''s more object based (which isn''t necessarily wors).

Also the pro you had for the handle approach is not relevant as either or ANY methodology should not allow "direct (miss-)use of the object". But I guess you where just clutching at straws for a reason to use the Handle way. Which is really only necessary in the C environment, which requires a means to hide information in the structs, when using C++ just use private/protected members.

i use a similar approach to what has been described here for managing my resources (load for first request, storing identifier in an STL map, and any subsequent requests for that resource are given a reference to the one previously loaded... i use reference counting to handle cleanup)

the way i generally get around the problem of the object being tampered with via the reference handed out is to simply pass it back as a const reference... then the other object cannot alter its state.

when an object is done with a resource (ie being destroyed), it must release it through the resource manager (and even though the resource may not actually be deleted, i treat it as such once release has been called).
quote:
2) I like to assert pointers in my debug builds, and with the static accessor I can to that at one centralized place (i.e. the accessor function itself) and don't have to do it in every function that uses it.


Yes, that does sound good. It is a fact, every function I pass my Renderer to, I check it for NULL. But this is where it comes down to personal preference. Once again, I must defend having the pointer or reference being passed in so I know what functions need it. Those two lines of code really do not take that long to copy and paste.

quote:
the way i generally get around the problem of the object being tampered with via the reference handed out is to simply pass it back as a const reference... then the other object cannot alter its state.


Yes, using const everywhere is good practice. It not only helps the compiler in some cases, it increases good coding habits, and stops people from doing stupid things.

EDIT: Got the quotes working.



Edited by - taulin on February 26, 2002 8:35:00 PM
quote:Original post by VizOne
I didn''t understand that exactly. Could you explain?


You are trying to do reference counting for the object handle you passed out only. You don''t need it to use smart pointers for the internal storage.

There are many approaches to handling this. I would go for the cleaner approach where the renderer would keep track of the reference count, rather than through the resource accessor.
After playing with the implementation a bit, I decided not to use smart pointers. As it has come clear to me that I need access to details of the objects (meshes, texture) nowwhere but *inside* the renderer class itself, I''ll you some kind of simple handle technique or - more low-level - simply pointers.


@Void: /me thinks you''re quite right. I''ll try a cleaner way.


@Taulin: of course "I" created the pointer and should deal with it correctly. Nevertheless I like the Idea of having the instance pointer within the instanciated object (mainly for a simple singleton implementation - the class should know, if it was instanciated once). BTW: as I''m not the only one that''ll use the lib. The renderer is a part of a complete game-framework, using some techniques of the MFC but being specialized for gamedev. It''s quite modular, so you only have to link to the parts you need. Some modules are app and window, input (DInput8), network (DPlay8), gfx (D3D8), sound (DMusic/DSound8), a virtual file-system (with archive support) ans so on (detail on my HP (only German now...). You got the point *g*. That''s why I want some sorta safety. And you know what they say: trust no one!


@d9930380:
quote:
As you can''t extend any of the classes unless you can modify the CRenderer class to add in extra:

as the lib (see above) can be extended the way you need, that''s exactly what I want: the programmer using the lib *will* extend the renderer class to his needs.

Bye, VizOne
Andre Loker | Personal blog on .NET
Well... im knee-deep in my first project and i just recently read this article titled: "A Generic Handle-Based Resource Manager" from here

http://www.drizzle.com/~scottb/publish/gpgems1_fubi.htm

The template approach is unbeatable, just solved my dependency problem with resource classes and a separate renderer class & sound class. The example there uses handles, and i thought about changing it back to pointers... and the invalid reference problem shows up (an object erroneously tries to use an already fred pointer), so then i thought about using const pointers, but the invalid references problem would still be there.

Then i thought about const proxy pointers... and keeping a list of used pointers... and then all the overhead is really similar to handles(except for a few extra stack calls). So... i guess i''ll go with handles.

so im going with a templatized resource manager, instantiated in each class (example, texture manager is instantiated by the renderer), with handles, reference counting and name mapping.

GDnewb::Madster
I''ve been reading through here, and I don''t recall anyone mentioning a nice benefit of handles; they allow you to save information to a file, while a simple pointer does not. ie; A texture manager should have the same handle to each texture each time it''s loaded. If you want to save some game data to a file (ie; what texture an orc has), then it''s as simple as saving the handle.

In any case, smart pointers should still be used. You will eventually pass a pointer to the caller w/ the id of the managed object, and that pointer should be a smart pointer IMO. The risk of dangling pointers is just too great, and smart pointers solve quite a few memory leak problems. Not to mention, they make pointer problems much easier to debug.

Just download boost (search google, but I think the URL is www.boost.org), if nothing else for their SmartPtr class.

This topic is closed to new replies.

Advertisement