auto_ptr Nasties!

Started by
13 comments, last by Anon Mike 18 years, 6 months ago
Hi, This is a brief discussion about an auto_ptr nasty that i don't know how to resolve. It also leads onto a com_ptr nasty. So supposing you have the following code: main.cpp

#include <memory>

void foo( int** ppInt )
{
  // ...
}

int main( int argc, char* argv[] )
{
 
 auto_ptr< int > pInt;

 pInt.reset( new int );

 // How do you get the address of the T* in auto_ptr. You can't do this:

 int** ppInt = &pInt; // this returns of type auto_ptr< int >*

 // You have to do this
 int* tempPInt = pInt.get();

 foo( &tempPInt );

 return 0;
}



To me this seems horrible because having to extract the contents of the auto_ptr defies the point in having it to begin with. Now you may argue that having architecture where this needs to be done, is in most cases unneeded and bad programming, but consider this. In the Direct3D API there are functions like D3DXCreateTextureFromFile(). One of the parameters to this is of type IDirect3DTexture**. If in your code you are using a com_ptr to clear up your reference counted memory then you have use a similar method to get T**. Now i wrote my own com_ptr, a shared pointer, in a similar style to auto_ptr. It works perfectly, is about as transparent as it can get but i am still undecided about whether to overload operator& for it. If i do overload it then the above T** problem doesn't exist because i can return T** from the operator. With this comes a problem though. If i want to return the address of the com_ptr from a class accessor i cant, because & is overloaded and therefore the accessor returns T** instead of com_ptr< T >*. I hope you understand this horridness, is there any way i can get around this. What would/do you do? ace
Advertisement
You could also use &* to convert a smart pointer to a normal pointer. &** would give the address of the pointer, but maybe only of a temprary copy of it. It should work if you let the * function return a non-const reference, but this is very dangerous; as anyone can bypass the smart pointer and modify what is inside it.
I don't know COM, but I assume these are out pointers? You probably don't want to give the COM function direct access to the internals to the smart pointer, this could lead to al kinds of problems. Store the result in a normal pointer first, and put it in the smart pointer only after the call.
I guess, that would work. But the idea of smart pointers is to manage the pointer from the beginning of its life time. So surely creating a temporary and giving it value with D3DX... then assigning it to a smart pointer is bad.

This whole idiom irritates.

ace
std::auto_ptr sole purpose is to implement resource acquisition is initialization (RAII) and exception safe code (which are not mutually exclusive). std::auto_ptr does not maintain reference counts, it use transfer of ownerships semantics. Despite the bashing std::auto_ptr gets it does exactly what it was set out to do and no more.

Generally you would not use std::auto_ptr with DX because as far as i'm aware DX objects are COM objects (i don't think they are true COM objects though), these objects already have an embedded reference count (hence the addRef & Release). In which case you could either use boost::shared_ptr and read the Using a shared_ptr to hold a pointer to a COM Object or use boost::intrusive_ptr which is like shared_ptr but for objects that already have a reference count (exactly like COM objects), boost::intrusive_ptr just makes the original system automated.

So if i was you i'd use boost::intrusive_ptr or something that does the same as it and in those cases like D3DXCreateTextureFromFile you can simply do:

LPDIRECT3DTEXTURE9 tex_tmp;D3DXCreateTextureFromFile(...., &tex_tmp);my_intru_ptr<IDirect3DTexture9> tex_ptr(tex_tmp);


Or something similar to that.
Sorry you misunderstand snk_kid, but thanks anyway, it was a mistake on my part.

It was stupid of me to say that i wrote the com_ptr in the same style as auto_ptr. I am completely aware of the sole purpose and methods behind the operation of auto_ptr. I am also aware of how a com_ptr should work. I wrote com_ptr to manage the reference counts as it should.

The question here is more a case of how is it best to attain the value of T** from either of these smart pointers as demonstrated in my code. Like i said, you would have to create a temporary if you do not overload&, then get the address of the temporary. If you do overload operator& then you cannot attain the & of the smart pointer.

I hope you understand.

ace
If you allow access to the pointer itself you could write:
com_ptr<Interface> ptr;ptr = allocate_large_object(); // increments reference countfoo(&ptr); // stores other object in ptr, reference count of large object not decremented

That is not a good thing. A possible solution is to use a special type for out parameters:
com_out_ptr<Interface> temp_ptr;com_ptr<Interface> ptr;foo(&temp_ptr);ptr = temp_ptr;

The code for com_out_ptr would look something like:
template <typename T>class com_out_ptr {  private:    T* ptr;    destroy() {        if (ptr) ptr->Release();        ptr = 0;    }    friend class com_ptr<T>;    com_out_ptr(const com_out_ptr&) {} // not copy-constructable  public:    com_out_ptr() : ptr(0) {}    ~com_out_ptr() { destroy(); }    T** operator &() {        destroy(); // make sure we no longer hold anything before giving access to the pointer        return &ptr;    }};// com_ptr can be constructed from a com_out_ptrtemplate <typename T>class com_ptr {    // ...    com_ptr(const com_out_ptr<T>& p) {        // take ownership from the         ptr = p.ptr;        const_cast<com_out_ptr<T>&>(p).ptr = 0;    }}
Quote:Original post by ace_lovegrove
The question here is more a case of how is it best to attain the value of T** from either of these smart pointers as demonstrated in my code. Like i said, you would have to create a temporary if you do not overload&, then get the address of the temporary. If you do overload operator& then you cannot attain the & of the smart pointer.


Yes i recommend you don't not overload the address-of operator the "get" method and taking the address of the underlaying pointer is at least explicit and makes finding subtle bugs easier. The only problem is i don't know what D3DXCreateTextureFromFile does if a pointer already refers to a DX object. If it deals with it inside then it's simple a case of:

D3DXCreateTextureFromFile(...., &my_smart_ptr.get());


I really thinking overloading the address-of operator is a bad idea.
See that is what i was thinking. If i had another com_ptr whose local is of type T**, then i could more easily associate it with 'out' parameters. Maybe the com_out_ptr() as you suggested could actually contain another com_ptr, or at least be created from another com_ptr.

Another light idea was to store the pointer as T (LPDIRECT3DDEVICE9) rather than T* (IDirect3DDevice9).

What do you think?

ace
Quote:Original post by snk_kid
Quote:Original post by ace_lovegrove
The question here is more a case of how is it best to attain the value of T** from either of these smart pointers as demonstrated in my code. Like i said, you would have to create a temporary if you do not overload&, then get the address of the temporary. If you do overload operator& then you cannot attain the & of the smart pointer.


Yes i recommend you don't not overload the address-of operator the "get" method and taking the address of the underlaying pointer is at least explicit and makes finding subtle bugs easier. The only problem is i don't know what D3DXCreateTextureFromFile does if a pointer already refers to a DX object. If it deals with it inside then it's simple a case of:

D3DXCreateTextureFromFile(...., &my_smart_ptr.get());


I really thinking overloading the address-of operator is a bad idea.


I completely agree. It has caused no end of problems, including this one [smile]. The solution there in the code snippet seems awefully simple to me, i could have sworn i tried that.

ace
Quote:Original post by ace_lovegrove
The solution there in the code snippet seems awefully simple to me, i could have sworn i tried that.


Yeah this wont work for anything like std::auto_ptr, if you com_ptr is like boost::intrusive_ptr that just automates AddRef/Release and D3DXCreateTextureFromFile handles things internally that snippet should work with no problems. Just remember that the return type needs to be a reference to a pointer otherwise you'll get complaints about temporaries

This topic is closed to new replies.

Advertisement