Arrays and gcroot

G

Guest

I am storing an array of strings in an unmanaged MFC class using gcroot as
follows:
gcroot<array<System::String^>^> m_pArr;

Nothing out of the ordinary there. However, if I try to use "delete m_pArr"
in the code in order to deterministically delete it, I get the following
error in VS05:
error C2440: 'delete' : cannot convert from 'gcroot<T>' to 'void *'

If I use the same with something other than an array i.e. using gcroot and
then doing a delete, its fine.

Any ideas on why the compiler is throwing this error?

Thanks.
 
A

adebaene

I am storing an array of strings in an unmanaged MFC class using gcroot as
follows:
gcroot<array<System::String^>^> m_pArr;

Nothing out of the ordinary there. However, if I try to use "delete m_pArr"
in the code in order to deterministically delete it, I get the following
error in VS05:
error C2440: 'delete' : cannot convert from 'gcroot<T>' to 'void *'

Well, gcroot is not a pointer type, so there is no way to call delete
on it. OTOH, gcroot will release the managed memory it holds when it
goes out of scope (when it's destructor is called), or when you assign
nullptr to it. Note that this will only release the GCHandle to the
managed object, so:
- if the managed object is still referenced somewhere else, it won't
be garbage-collected.
- even if there are no other references, the managed object won't be
deleted immediatly : it will just be marked as ready for garbage-
collection.
- the managed object's Dispose method won't be called immediatly (if
implemented).

If you need true ownership semantics on the managed object, you should
use auto_gcroot (with all the same subtle caveats as std::auto_ptr
concerning weird ownership semantic).
If I use the same with something other than an array i.e. using gcroot and
then doing a delete, its fine.
No it isn't. I believe that in the other case, you are declaring a
pointer to a gcroot (eg, " gcroot<MyStuff^>* " - note the final
pointer star).

Please show us some code and explain what it is you are trying to
achieve if you need more help.

Arnaud
MVP - VC
 
B

Ben Voigt

I am storing an array of strings in an unmanaged MFC class using gcroot as
follows:
gcroot<array<System::String^>^> m_pArr;

Nothing out of the ordinary there. However, if I try to use "delete
m_pArr"
in the code in order to deterministically delete it, I get the following
error in VS05:
error C2440: 'delete' : cannot convert from 'gcroot<T>' to 'void *'

For any gcroot<T^> instance
Try
delete (T^) instance;
to dispose instance deterministically. The cast explicitly invokes the user
defined conversion.
Better yet, to avoid the C-style cast,
delete instance->operator T^();
I wish there was a user_cast<>() operator for that. Probably can make one
easily enough with templates that works for any type, but I doubt that
modifiers would be handled properly.

Anyway, CLI arrays don't implement IDisposable, even when the member type
does, so in your particular case there's no need to call delete at all. But
this method has an advantage over calling Dispose directly in that it can be
used in a template that accepts any managed type whether or not it
implements IDisposable.
 
T

Tamas Demjen

Well, gcroot is not a pointer type, so there is no way to call delete
on it.

My understanding is that calling delete on a gcroot actually calls
Dispose on the contained managed object. Obviously this only makes sense
if gcroot holds an IDisposable object. array<>^ and String^ are not
IDisposable and they don't need deterministic destruction, they're
simply garbage collected.

The way I understand it is that
- gcroot automatically releases the GCHandle that it manages (decreasing
its reference counter), but it doesn't Dispose.
- auto_gcroot not only releases the GCHandle, but it also calls Dispose
on the contained object.

Tom
 
B

Ben Voigt

Tamas Demjen said:
My understanding is that calling delete on a gcroot actually calls Dispose
on the contained managed object. Obviously this only makes sense if gcroot
holds an IDisposable object. array<>^ and String^ are not IDisposable and
they don't need deterministic destruction, they're simply garbage
collected.

delete is an operator, and thus the compiler semantics for its use can't be
changed by gcroot, which is a template class and not a true keyword. delete
can only be used with pointers and tracking handles. So if you want to
dispose an object held by a gcroot, you'll have to get the tracking handle
out first.
 
T

Tamas Demjen

Ben said:
delete is an operator, and thus the compiler semantics for its use can't be
changed by gcroot, which is a template class and not a true keyword. delete
can only be used with pointers and tracking handles. So if you want to
dispose an object held by a gcroot, you'll have to get the tracking handle
out first.

A conversion operator can introduce pointer or tracking handle semantics:

struct C
{
operator int* () const { return 0; }
};

void test()
// do not run this, but it is legal code that compiles
{
C c;
delete c; // operator int* is implicitly called here
}

This is contraversial, and most smart pointer implementations don't have
such a conversion operator. gcroot<T>, however, provides it: "operator T
() const". That's why you could normally call delete on a gcroot, and it
would work:

ref class Managed
{
public:
~Managed() { }
};
gcroot<Managed^> m;
delete m; // this is OK, it calls Managed::Dispose,
/ /via the implicit conversion operator T() const

However, when Managed is an array or String, or anything that doesn't
support the Dispose pattern, it won't work. That's when you get the
"cannot convert from 'gcroot<T>' to 'void*'" error:

gcroot<array<String^>^> m;
delete m; // this causes an error, array can't be deleted

auto_gcroot itself calls delete, but it uses an explicit conversion:
delete _element_type(m_ptr);

Tom
 
B

Ben Voigt

Tamas Demjen said:
A conversion operator can introduce pointer or tracking handle semantics:

struct C
{
operator int* () const { return 0; }
};

void test()
// do not run this, but it is legal code that compiles
{
C c;
delete c; // operator int* is implicitly called here
}

Yes, you're right, delete can cause an implicit conversion (I didn't expect
that).

The explicit conversion is still much clearer.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top