   Handles vs. SmartPointers - the discussion ;-)
  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 mind1) 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 instanceCRenderer renderer;// renderer creates a mesh-object and returns the handle that this object is attached toHMESHOBJECT hMesh = renderer.CreateMeshObject();// renderer loads geometrie data or whatever to the object attached to hMeshrenderer.LoadMeshData(hMesh, "mesh001.dat");// renderer actually renders the object attached to hMeshrenderer.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 CRendererCRenderer 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" dataspMesh->LoadData("mesh001.dat");// CMeshObject implements functions that provide its protected data to the renderer// e.g. CMeshObject::GetVertexBuffer() or somethingrenderer.Render(spMesh);    Let's compare the pros&consHANDLE~~~~~~pro- no direct (miss-)use of the object is possiblecon- 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 OOPishsmart-pointers~~~~~~~~~~~~~~pro- direct access to the object- more OOPishcon- ??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 advanceBye, VizOneEdited by - VizOne on February 24, 2002 8:29:31 AM  
  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!    ;  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  ;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.  ;  Wow, I am glad someone else brought this topic up.  I tried to:http://www.gamedev.net/community/forums/topic.asp?topic_id=80728But 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 Entityalso 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.  ;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  ;  Sorry, i thought Attach() was a D3D method... i should learn to read ;quote:Original post by Spiral  Sorry, i thought Attach() was a D3D method... i should learn to read   no prob :D  ;  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.;  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.  