Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

VizOne

Handles vs. SmartPointers - the discussion ;-)

This topic is 5272 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello! We (i.e. me and a pal) are devoloping a class that wraps up Direct3D (in C++). Though the "problem" I ask you to help us with is more likely a general one, that's why post into this forum. Besides the initialization of Direct3D etc. the class (let's call CRenderer) shall keep track of other objects related to the renderer (e.g. CMeshObject) internally. That is, it shall create an instance of CMeshObject or whatever and adds it (more likely its pointer) to a list or a vector or something. The question actually is how to provide access to these objects in quite an elegant way. We two quite similar ways in mind 1) Handles. All the interaction with the objects in CRenderer is handled through handles (sounds funny *g*). We'd use the handle technique Windows uses as well (for those who don't know: search for #define DELCLARE_HANDLE in the windows includes). An example of using it would be:
// the renderer instance
CRenderer renderer;

// renderer creates a mesh-object and returns the handle that this object is attached to
HMESHOBJECT hMesh = renderer.CreateMeshObject();

// renderer loads geometrie data or whatever to the object attached to hMesh
renderer.LoadMeshData(hMesh, "mesh001.dat");
// renderer actually renders the object attached to hMesh
renderer.RenderMesh(hMesh);
  
It's not that cool, but I think you got it 2) smartpointers. Another way to handle the objects would be using smartpointers. (BTW:Normal pointers would have been too unsafe). Our smartpointer implementation is this one An example code might be:
// the instance of CRenderer
CRenderer renderer;

// renderer creates an CMeshObject instance, adds a smart-ptr to that object into its internal list and returns a copy
// of the smart-ptr (CSmartPtr is only 4Byte so copying has no big performance penalty)

CSmartPtr spMesh = renderer.CreateMeshObject();

// the object loads "its" data
spMesh->LoadData("mesh001.dat");

// CMeshObject implements functions that provide its protected data to the renderer
// e.g. CMeshObject::GetVertexBuffer() or something
renderer.Render(spMesh);
  
Let's compare the pros&cons HANDLE ~~~~~~ pro - no direct (miss-)use of the object is possible con - no direct use of the object is possible (yes, it's also a con...) - the renderer has to implement all the stuff to deal with the objects, i.e. everytime you modify a property of the object, you'll need to do it through the renderer - it's not very OOPish smart-pointers ~~~~~~~~~~~~~~ pro - direct access to the object - more OOPish con - ?? Both methods are safe as the renderer keeps track of the objects internally and destroys them at the end of the app or when needed. Well, I personally like the smart-pointer method best. That's why my comparison chart is... erm... not very neutral. But I'd like to encourage you to tell us your opinion. Which way would you prefer and why? Thanks in advance Bye, VizOne Edited by - VizOne on February 24, 2002 8:29:31 AM

Share this post


Link to post
Share on other sites
Advertisement
I just thought I''d let you know about CComPtr (for COM-based objects).
#include <atlbase.h>
//
...
CComPtr<IDirect3D8> pD3D;
CComPtr<IDirect3DDevice8> pDevice;
...
// this is the only pointer that needs to be attached:
pD3D.p->Attach( ::Direct3DCreate8(...) );
//
// all others can be assigned normally:
pD3D.p->CreateDevice( ..., &(pDevice.p) );

Using this facility you''ll probably have less maintenance to do. Why? Because CComPtr calls Release on the controlled pointer, as well as a few other functions. There''s also CComQIPtr, which supports calling QueryInterface.

[ GDNet Start Here | GDNet Search Tool | GDNet FAQ | MS RTFM [MSDN] | SGI STL Docs | Google! ]
Thanks to Kylotan for the idea!

Share this post


Link to post
Share on other sites
Well, that's not exactly what I intended this thread to be for but thanks a lot. Nice to know about this sucker!

quote:
Original post by Oluseyi

// this is the only pointer that needs to be attached:
pD3D.p->Attach( ::Direct3DCreate8(...) );
//



Not sure, but shouldn't it be pD3D.Attach(...), as Attach is a method of CComPtr and not of IDirect3D8 (or more generally T*)?

Bye, VizOne

Edited by - VizOne on February 25, 2002 2:31:22 AM

Share this post


Link to post
Share on other sites
quote:
Original post by VizOne
Not sure, but shouldn''t it be pD3D.Attach(...), as Attach is a method of CComPtr and not of IDirect3D8 (or more generally T*)?



No.

Share this post


Link to post
Share on other sites
Wow, I am glad someone else brought this topic up. I tried to:

http://www.gamedev.net/community/forums/topic.asp?topic_id=80728

But as you see (if you read it), I did not get any replies.
I have been recently trying the handle method. My ''CRender (as you put it)'' has a Manager for each major resource type (TextureManager, VBManager, etc). These managers are used to create, restore and manage their respective resources.

I have only a couple of worries about my system.
First, I pass a pointer to CRender around to any function that does a graphic type job. Yes, the CRender holds my managers, and it gives a pointer of itself to them when, for example, SetTexture( TextureHandle ) is called, like a passthrough.

Example :

My Entity has a function Render( CRender). The Entity
also has the texture handle it needs, so it will call CRender.SetTexture( m_textHandle );

In turn, the CRender, will just call the Manager..
CRender::SetTexture( textureHandle )
{ TexManager( this, textureHandle ); }

TexManager then finds the resource (currently using an STL map), and gets a DXResource texture pointer. The manager also checks to see if this resource is already setup, reducing search and state changes. As another bonus, if the manager is given a duplicate resource to load, it will not load it twice, and give back the correct handle.

However, besided that, the system works great. The rest of my game engine never worries about the type of VB or texture it uses. Nor does it worry about how to manage them in case surfaces were lost, and what not. Having the CRender class hold the managers means all the DirectGraphics stuff is close together.

So, like you, I have been happy with the design, but worried about the means. Overall, I only have about 2 or 3 passthrough functions per manager, which is not really bad. I was going to create interfaces, but I still wanted to use seperate manager classes. Using handles, and having these managers has really added many pluses to my game architecture as a whole.

Share this post


Link to post
Share on other sites
quote:
Original post by Spiral

No.



No?

The declaration of CComPtr (in atlbase.h) says:


...
void Attach(T* p2)
{
if (p)
p->Release();
p = p2;
}
...
T* p;

(T is the templated type)

@Taulin: your implementation almost exactly matches what we're doing/planning to do. You may overcome the pointer-passing problem in this way: implement CRenderer as a singleton (you'd only want one instance at a time for sure) and give CRenderer a static pointer to its one and only instance ( static CRenderer * m_pInstance; ). Assign "this" to it at creation (in the c'tor or a static creation function). Then provide a function that accesses this pointer or a reference to the instance (static CRenderer & GetInstance(){ ASSERT(m_pInstance); return *m_pInstance} ). With this, you can access the renderer from everywhere without global vars and you don't need passing pointers anymore.



Edited by - VizOne on February 25, 2002 5:07:49 AM

Share this post


Link to post
Share on other sites
quote:
Original post by Spiral
Sorry, i thought Attach() was a D3D method... i should learn to read


no prob :D

Share this post


Link to post
Share on other sites
About the singleton use; I understand what you are getting at, and it seems like a good idea, but I must ask your oppinion on a different standpoint. I do not pass the CRenderer everywhere (almost everywhere since it handles creation of resources and the rest), but not everywhere (like network code, etc). Passing a pointer does not take much time or resources, so that is not a factor. 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.

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.

Could you explain why you think using a globally acceesable reference is better than passing a pointer explicitly? One reason I can think of is to cut down on the massive amount of pointer passing, but at that point, better engine architecture is in order.

Share this post


Link to post
Share on other sites
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?

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

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

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!