Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > smart pointer with custom deconstruction?

Reply
Thread Tools

smart pointer with custom deconstruction?

 
 
Christopher
Guest
Posts: n/a
 
      02-27-2009
I have a need to create a method that allocates something, but niether
the method or the class is responsible for managing its lifetime. I
feel like returning a raw pointer is a bad idea. So, I thought that I
could wrap it up in a smart poiner of some kind. However, the thing
that I am allocating is from a COM object, so I cannot just call
delete on it, instead if reuqires I call it's Release() method. Is
there any kind of pointer class in existance already there I can
customize its destruction to call the Release method on my allocated
object?

I just don't want my reviewer gasping at my returning a raw pointer.
 
Reply With Quote
 
 
 
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      02-27-2009
Christopher wrote:

[snip]
> Is there any kind of pointer class in existance already there I can
> customize its destruction to call the Release method on my allocated
> object?

[snip]

Sure: both, tr1::shared_ptr and boost::shared_ptr allow for a custom
deleter. You pass the deleter as an argument upon construction of the
shared_ptr.


Best

Kai-Uwe Bux
 
Reply With Quote
 
 
 
 
Christopher
Guest
Posts: n/a
 
      02-27-2009
On Feb 27, 3:51*pm, Kai-Uwe Bux <(E-Mail Removed)> wrote:
> Christopher wrote:
>
> [snip]> Is there any kind of pointer class in existance already there I can
> > customize its destruction to call the Release method on my allocated
> > object?

>
> [snip]
>
> Sure: both, tr1::shared_ptr and boost::shared_ptr allow for a custom
> deleter. You pass the deleter as an argument upon construction of the
> shared_ptr.


A question related to this is:
If I have a hierarchy of objects that need to use the object I got a
smart pointer for, but only one manages its lifetime, would it be best
practice to pass and store a reference in each dependant or pass and
store the smart pointer?

I know there is some tiny overhead with smart pointers, but the number
of objects that are going to use it is tremendous. It is used in
almost every class in my project, with possibly up to 5k or so
instances. I know one person's opinion at work was to just store a
reference while others passed the smart pointer around. What are your
thoughts?

P.S.
Kai-Uwe - You have been a tremendous help in a number of posts. You
rock!
 
Reply With Quote
 
Thomas J. Gritzan
Guest
Posts: n/a
 
      02-28-2009
Christopher schrieb:
> I have a need to create a method that allocates something, but niether
> the method or the class is responsible for managing its lifetime. I
> feel like returning a raw pointer is a bad idea. So, I thought that I
> could wrap it up in a smart poiner of some kind. However, the thing
> that I am allocating is from a COM object, so I cannot just call
> delete on it, instead if reuqires I call it's Release() method. Is
> there any kind of pointer class in existance already there I can
> customize its destruction to call the Release method on my allocated
> object?


If you don't mind using ATL, the obvious choice would be CComPtr. This
smart pointer is designed to handle COM objects and has smaller overhead
since it's an intrusive pointer.
Please ask futher specific questions on ATL and COM in a Microsoft
newsgroup, since it is hardly on-topic here.

If you don't want to use ATL, then another intrusive smart pointer like
boost::intrusive_ptr would be the next best idea, since COM objects
already carry a reference count.

Intrusive pointers are small, essentially they only hold the actual raw
pointer, incrementing or decrementing the reference count when copied or
destroyed.

--
Thomas
 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      02-28-2009
Christopher wrote:

> On Feb 27, 3:51*pm, Kai-Uwe Bux <(E-Mail Removed)> wrote:
>> Christopher wrote:
>>
>> [snip]> Is there any kind of pointer class in existance already there I
>> [can
>> > customize its destruction to call the Release method on my allocated
>> > object?

>>
>> [snip]
>>
>> Sure: both, tr1::shared_ptr and boost::shared_ptr allow for a custom
>> deleter. You pass the deleter as an argument upon construction of the
>> shared_ptr.

>
> A question related to this is:
> If I have a hierarchy of objects that need to use the object I got a
> smart pointer for, but only one manages its lifetime, would it be best
> practice to pass and store a reference in each dependant or pass and
> store the smart pointer?


I don't exactly understand what you mean by "only one manages its lifetime".

> I know there is some tiny overhead with smart pointers, but the number
> of objects that are going to use it is tremendous. It is used in
> almost every class in my project, with possibly up to 5k or so
> instances. I know one person's opinion at work was to just store a
> reference while others passed the smart pointer around. What are your
> thoughts?


First, we need to be clear on the rationale for using a shared_ptr in the
first place. From your first post:

I have a need to create a method that allocates something, but niether
the method or the class is responsible for managing its lifetime. I
feel like returning a raw pointer is a bad idea. So, I thought that I
could wrap it up in a smart poiner of some kind. However, the thing
that I am allocating is from a COM object, so I cannot just call
delete on it, instead if reuqires I call it's Release() method.

Usually, one would use auto_ptr for returning a pointer from a creation
function. One would do so to support exception safe coding. Unfortunately,
auto_ptr does not have a custom deleter. That is the reason to use
shared_ptr instead (come to think of it, unique_ptr is probably a better
choice!).

Now, with auto_ptr, one would release the pointer once the critical phase is
over. I would proceed similarly with shared_ptr or unique_ptr in this case.
In particular, I would not use shared_ptr members at all. The reason would
be that shared_ptr members would convey the message that the shared_ptr is
managing the lifetime and that "deletion upon loss of last reference" is
the ownership model used. If you have a different model (and that seems to
be the case), then your code should convey that; and therefore, shared_ptr
would be confusing.

If you use unique_ptr instead, the question would not even arise. I think,
you may want to have a look into unique_ptr.


That said: using reference members or pointer members can be very tricky. Be
sure to employ rigorous reasoning and testing to ensure that you don't leak
resources and that you don't access objects that died already. I find, that
encapsulating the ownership and lifetime management issues into smart
pointer classes can be very helpful. However, that smart pointer is not
very often shared_ptr.


Best

Kai-Uwe Bux
 
Reply With Quote
 
Thomas J. Gritzan
Guest
Posts: n/a
 
      02-28-2009
Kai-Uwe Bux schrieb:
> Christopher wrote:
>
> [snip]
>> Is there any kind of pointer class in existance already there I can
>> customize its destruction to call the Release method on my allocated
>> object?

> [snip]
>
> Sure: both, tr1::shared_ptr and boost::shared_ptr allow for a custom
> deleter. You pass the deleter as an argument upon construction of the
> shared_ptr.


Since the OP asked about COM objects (which are those pseudo C++ classes
based on the interface IUnknown, widely used on Microsoft Windows), I
advice against using shared_ptr with them.

COM objects already carry a reference count, so an intrusive pointer
that can be adapted to the IUnknown interface would be a better idea.

Besides, the designers of COM forgot to protect the objects against
acidentally 'delete'ing them. Thus when wrapping a pointer in a
shared_ptr, an omitted custom deleter won't be captured at compile time.

--
Thomas
 
Reply With Quote
 
Christopher
Guest
Posts: n/a
 
      02-28-2009
On Feb 27, 3:51*pm, Kai-Uwe Bux <(E-Mail Removed)> wrote:
> Usually, one would use auto_ptr for returning a pointer from a creation
> function. One would do so to support exception safe coding. Unfortunately,
> auto_ptr does not have a custom deleter. That is the reason to use
> shared_ptr instead (come to think of it, unique_ptr is probably a better
> choice!).


I looked up unique_ptr and it says:
"A unique_ptr is not CopyConstructible, nor CopyAssignable, however it
is MoveConstructible and Move-Assignable"

Ok no problem, but how do I move it? What do those terms
MoveConstructable and Move-Assignable mean?
How do I pass it around while being positive than it does not destroy
itself until I want it to do so?

If class A contains and manages the lifetime of the allocated object
class B provides a creation method that allocates the object
class A needs to contain the "master", but passes the allocated object
to its dependants for them to use
 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      02-28-2009
Christopher wrote:

> On Feb 27, 3:51*pm, Kai-Uwe Bux <(E-Mail Removed)> wrote:
>> Usually, one would use auto_ptr for returning a pointer from a creation
>> function. One would do so to support exception safe coding.
>> Unfortunately, auto_ptr does not have a custom deleter. That is the
>> reason to use shared_ptr instead (come to think of it, unique_ptr is
>> probably a better choice!).

>
> I looked up unique_ptr and it says:
> "A unique_ptr is not CopyConstructible, nor CopyAssignable, however it
> is MoveConstructible and Move-Assignable"
>
> Ok no problem, but how do I move it? What do those terms
> MoveConstructable and Move-Assignable mean?
> How do I pass it around while being positive than it does not destroy
> itself until I want it to do so?


I have yet to learn C++0X, but I think these are concepts describing the
assignment operator and the constructor. So, you would move them using the
old syntax of

assignment
returning a value from a function
constructing an object from a source (but the source would be altered)


> If class A contains and manages the lifetime of the allocated object
> class B provides a creation method that allocates the object
> class A needs to contain the "master", but passes the allocated object
> to its dependants for them to use


Huh? That description is too high level for me. Can you illustrate that in
code? Also, what would be the question?


Best

Kai-Uwe Bux

 
Reply With Quote
 
Christopher
Guest
Posts: n/a
 
      02-28-2009
On Feb 28, 1:45*am, Kai-Uwe Bux <(E-Mail Removed)> wrote:
> Christopher wrote:
> > If class A contains and manages the lifetime of the allocated object
> > class B provides a creation method that allocates the object
> > class A needs to contain the "master", but passes the allocated object
> > to its dependants for them to use

>
> Huh? That description is too high level for me. Can you illustrate that in
> code? Also, what would be the question?


Well I am tying to illistrate what the relationships I am dealing
with.
Quations - Is using a unique_ptr with a custom deallocater is the best
option here? (so far it looks that way, trying to be certain)
Whether and how to pass a reference without deallocating,
when using the unique_ptr, to dependant objects?

My real code would probably require more than a 1000 lines to
illistrate, so I will do my best to get the idea across in minimal
snippets. I doubt it is compilable. I can't think of a compilable
example to demonstrate what I am talking about, where it would make
sense.

-------------------------------
I've got a DisplayModeEnumerator class, which enumerates supported
display mode attributes for my DirectX specific application.
It has to retrieve a COM interface to a Direct3D device in order to
perform its duties as noted in the private method.
Since it keeps track of valid devices, and display mode attributes for
that device, and has to get an interface ot it anyway... I was
thinking it would be a good place to perform the duty of getting the
device in my project (make it a factory).


#ifndef DISPLAYMODEENUMERATOR_H
#define DISPLAYMODEENUMERATOR_H

// SNIP

// Windows Includes
#include <dxgi.h>

// Boost Includes
#include <boost/shared_ptr.hpp>

// SNIP

//------------------------------------------------------------------------------------------

// Enable boost::mem_fn for use of smart pointers with COM objects
#ifndef BOOST_MEM_FN_ENABLE_STDCALL
#define BOOST_MEM_FN_ENABLE_STDCALL
#endif

/**
* Shared pointer to a D3D10 device
*
* When creating this shared pointer, be sure to give it a custom
deallocater that calls the Release method of the device
* Syntax: ID3D10Device_SharedPtr(ID3D10Device, boost::mem_fn
(&ID3D10Device::Release));
*/
typedef boost::shared_ptr<ID3D10Device> ID3D10Device_SharedPtr;

//------------------------------------------------------------------------------------------
/**
* Display Mode Enumerator
*
* Queries the system for its display capabilites and provides an
interface to retrieve those capabilities
**/
class DisplayModeEnumerator
{
public:

/**
* Constructor
**/
DisplayModeEnumerator();

/**
* Deconstructor
**/
~DisplayModeEnumerator();


/**
* Gets the number of adapters
*
* @return unsigned - Number of supported adapters
**/
const unsigned GetNumAdapters() const;

// SNIP

private:


/**
* Creates a D3D device
*
* @param adapter - Adapter with which to create the device
*
* @return ID3D10Device_SharedPtr - boost_shared_ptr to a
ID3D10Device interface
**/
ID3D10Device_SharedPtr CreateDevice(IDXGIAdapter * adapter);

// SNIP
};

#endif // DISPLAYMODEENUMERATOR_H


---------------
I've got a GFXApplication class which the actual application can
derive from and serves as the manager and owner of all objects related
to rendering anything to the screen

#ifndef GFXAPPLICATION_H
#define GFXAPPLICATION_H

// EngineX Includes
#include "DisplayModeEnumerator.h"
#include "EffectManager.h"

// SNIP

//------------------------------------------------------------------------------------------
class GFXApplication
{
public:

/**
* Constructor
*/
GFXApplication(const std::string & title);

/**
* Deconstructor
*/
virtual ~GFXApplication();

/**
*
**/
void Init(HINSTANCE instance, const bool fullscreen, const unsigned
clientWidth, const unsigned clientHeight);

/**
* Starts the application run loop
*/
virtual int Run();

// SNIP

protected:

// SNIP

ID3D10Device * m_device; // D3D device
TextureManager * m_textureManager; // Contains all loaded textures
EffectManager * m_effectManager; // Contains all loaded effects

// SNIP
};

#endif


---------------
One example of a dependant is the EffectManager, which loads,
compiles, and manages "effects".
There are a plethora of classes like this one. A TexureManager, a
CameraManager, a ModelFactory, etc. All of which need access to the
device to perform thier duties. Note - Some people dislike the word
"Manager" in a class. I think we could safely replace it with factory.

#ifndef EFFECTMANAGER_H
#define EFFECTMANAGER_H

// EngineX Includes
// Not going to post these headers too, just including these for
illustration
#include "Graphics/Effects/Effect.h"
#include "Graphics/Textures/TextureManager.h"

// DirectX Includes
#include <d3dx10.h>
#include <d3d10.h>

// Standard Includes
#include <string>
#include <map>

//----------------------------------------------------------------------------
/**
* Manages the DirectX effects, which contain techniques for rendering
*/
class EffectManager
{
public:

/**
* Constructor
*
* @param device - an intialized Direct3D device to use
* @param effectsDirectory - Directory that contains the .fx
and .fxh files for the DirectX effects
*/
EffectManager(ID3D10Device & device, TextureManager &
textureManager, const std::string & effectDirectory);

/**
* Deconstructor
*/
~EffectManager();


/**
* Creates an effect from a file
*/
void CreateEffectFromFile(const std::string & effectName, const
std::string & filePath);

// SNIP

private:

ID3D10Device & m_device;
TextureManager & m_textureManager;

// SNIP

// Child effects
typedef std::map<std::string, Effect *> EffectMap;
EffectMap m_effects;
};

#endif // EFFECTMANAGER_H


-----

So, in this example code, the EffectManager and TextureManager are
members of the GFXApplication class. Both need a reference to the D3D
device for thier lifetime in order to perform thier duties. The
GFXApplication manages the lifetime of the D3D device. The
DisplayModeEnumerator creates the device and is called by the
GFXApplication.

So, I am wondering if I should keep passing references down the
hierarchy of classes.
I know lifetime management is an issue, but in this case it is fairly
easy - The device interface should not be released until the
application is ended.



 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      02-28-2009
Christopher wrote:

> On Feb 28, 1:45*am, Kai-Uwe Bux <(E-Mail Removed)> wrote:
>> Christopher wrote:
>> > If class A contains and manages the lifetime of the allocated object
>> > class B provides a creation method that allocates the object
>> > class A needs to contain the "master", but passes the allocated object
>> > to its dependants for them to use

>>
>> Huh? That description is too high level for me. Can you illustrate that
>> in code? Also, what would be the question?

>
> Well I am tying to illistrate what the relationships I am dealing
> with.
> Quations - Is using a unique_ptr with a custom deallocater is the best
> option here? (so far it looks that way, trying to be certain)


I agree, it looks that way.


> Whether and how to pass a reference without deallocating,
> when using the unique_ptr, to dependant objects?


I would try:

unique_ptr<T> up ( new .... );
...
T & t_ref ( *up );

The documentation I saw for boost says that the expression *up has
unspecified type, but the latest draft of the standard says that the type
is

add_lvalue_reference<T>::type

which resolves to T& for object types.


> My real code would probably require more than a 1000 lines to
> illistrate, so I will do my best to get the idea across in minimal
> snippets.


Thanks for the code (though I will snip it, I needed it to understand your
situation).

[snip]
> *
> * When creating this shared pointer, be sure to give it a custom
> deallocater that calls the Release method of the device
> * Syntax: ID3D10Device_SharedPtr(ID3D10Device, boost::mem_fn
> (&ID3D10Device::Release));
> */
> typedef boost::shared_ptr<ID3D10Device> ID3D10Device_SharedPtr;


(a) I think, that you would need to pass a second template argument.
(b) Does the Release() member function also deallocate the memory?
Otherwise, you might want to have a simple function

void dispose ( ID3D10Device * ptr ) {
ptr->Release();
delete ptr;
}

and pass &dispose to your smart pointer.

[snip]
> class GFXApplication
> protected:
>
> // SNIP
>
> ID3D10Device * m_device; // D3D device
> TextureManager * m_textureManager; // Contains all loaded textures
> EffectManager * m_effectManager; // Contains all loaded effects
>
> // SNIP
> };

[snip]
> So, in this example code, the EffectManager and TextureManager are
> members of the GFXApplication class. Both need a reference to the D3D
> device for thier lifetime in order to perform thier duties. The
> GFXApplication manages the lifetime of the D3D device. The
> DisplayModeEnumerator creates the device and is called by the
> GFXApplication.


I assume that the m_textureManager of a particular GFXApplication instance
will contain a reference to that instances *m_device. So, all dependent
objects can have synchronized lifetimes. In that case, I don't see what
would be wrong with references.


Best

Kai-Uwe Bux
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Never ever use a raw pointer when a smart pointer can do the same job Hicham Mouline C++ 100 08-25-2009 05:07 PM
Smart Pointer release() const : it can set the pointer to null with the keyword "const"? coala C++ 1 09-06-2006 03:00 PM
Smart Pointer release() const : it can set the pointer to null with the keyword "const"? coala C++ 3 09-06-2006 02:58 PM
Smart Card Certificate Logon and Smart Card Wireless EAP-TLS erha Wireless Networking 0 05-19-2005 01:40 AM
trade 64mb smart media for 16mb smart media cards wjva Digital Photography 1 08-20-2003 08:30 PM



Advertisments