Beta 2: Automatic Dispose inaction?

  • Thread starter Thread starter Alexei
  • Start date Start date
A

Alexei

Hello,

The following doesn't compile due to absence of the copy constructor in class FileStream:

FileInfo ^ fi = ...;
FileStream fs = fi->OpenRead();

The compiler is Beta 2. Is this supported? Planned to be supported?
 
Alexei said:
Hello,

The following doesn't compile due to absence of the copy constructor
in class FileStream:

FileInfo ^ fi = ...;
FileStream fs = fi->OpenRead();

FileStream^ fs = fi->OpenRead();

-cd
 
Alexei Zakharov said:
Don't answer just to answer. Look here: http://blogs.msdn.com/arich/ (DF
pattern).

This pattern is for C++/CLI implemented classes only, the FCL are written in
C# and their classes can't be "stack allocated". So, what you should do is,
allocate the object on the heap and manually dispose.

Willy.
 
Willy Denoyette said:
This pattern is for C++/CLI implemented classes only, the FCL are
written in C# and their classes can't be "stack allocated". So, what
you should do is, allocate the object on the heap and manually
dispose.

It doesn't seem plausible since I can write:
FileStream fs("myfile", FileMode::Create);
And it will work.
 
Alexei said:
It doesn't seem plausible since I can write:
FileStream fs("myfile", FileMode::Create);
And it will work.

Sorry for not being more explicit. The FileInfo->OpenRead returns a
FileStream object reference (heap allocated), you can't assign it to a stack
allocated variable.
So what you could do is...

FileInfo fi(".....");
FileStream ^fs = fi.OpenRead();

Willy.
 
Willy said:
Sorry for not being more explicit. The FileInfo->OpenRead returns a
FileStream object reference (heap allocated), you can't assign it to
a stack allocated variable.

There is no such thing as stack allocation for ref classes. So the whole
your argument is moot.

{
FileStream fs(...);
}

is equivalent to:

{
FileStream ^ fs = gcnew FileStream(...);
try {
} finally {
delete fs;
}
}

See, the object is still on GC heap. You can't place it on stack. What you
have is just a language construct that sort of looks like a stack-based
placement.

Now, does it matter if the object is created with gcnew or by calling a
function? No, IMO. The same code I can rewrite with using fi.OpenRead():

{
FileStream ^ fs = fi.OpenRead();
try {
} finally {
delete fs;
}
}

What leads me to simply:

FileStream fs(fi.OpenRead());

I can't understand the point of having copy-constructors for ref classes.
..NET object model is fundamentally different from C++ object model. There
is no copy-construction per se in .NET (besides trivial bitwise copy for
value classes). Objects are passed around by reference not by value as in
C++, so copying is not needed. I don't see the reason for C++/CLI to
introduce an idiom that can't have any relevence for .NET programming.
 
There is no such thing as stack allocation for ref classes.

The point is "stack semantics", not "stack allocation". The memory for
the object is indeed allocated from GC heap, but it is (mostly)
irrelevant here.
 
Who's talking about stack allocated reference classes, I said "assign it to
a stack allocated variable" right?
I know ref. classes aren't stack allocated, they have stack semantics.

For the compiler there's a difference between:
FileStream fs = fi->OpenRead();
and
FileStream fs(...);
The first is an assignment of a "stack allocated variable", with stack
semantics, to a value with "reference" semantics (of type FileStream),
returned by a (C#) function call. This is something that the current C++
compiler can't handled, why he's insisting on a copy constructor is beyond
me, ref class can't have copy constructors.
The latter is a constructor call, that upon return (note: nothing is
returned from the constructor!!!), initializes a stack allocated variable
with stack semantics. This initialization is completely handled by the C++
compiler.

Herewith a simple sample that illustrates the issue:

// C#
public class Tester
{
public Test GetTest()
{
Test t = new Test();
return t;
}
}

public class Test
{...
}
}

// C++
Tester t;
Test tt = t.GetTest();

Willy.
 
Alexei said:
Now, does it matter if the object is created with gcnew or by calling a
function? No, IMO. The same code I can rewrite with using fi.OpenRead():

{
FileStream ^ fs = fi.OpenRead();
try {
} finally {
delete fs;
}
}

Something like this would be nice, but it can't be done the way you're
trying to do it.
What leads me to simply:

FileStream fs(fi.OpenRead());

I think the code above would expand to

FileStream^ fs = gcnew FileStream(fi.OpenRead());
[...]

and there is no such copy constructor in FileStream (and even if there
was one, you wouldn't want to make a copy in this case).
I can't understand the point of having copy-constructors for ref classes.

There is such a thing as copy c'tor and assignment operator in C++/CLI.
Because what if you want to make a copy? You could always define a
static member (class method) name CopyMe, but C++/CLI was designed to be
as conforming with C++ as possible. The stack syntax with ref classes
has to work like stack objects in native C++, otherwise you're inventing
a new language that has nothing to do with C++. Exactly that happened
with the old MC++, where the destructor syntax was used for the
finalizer. Such a radical change has disadvantages, because C++
programmers have certain expectations about the language, such as the
copy c'tor syntax, even if it's implemented in the underlying .NET layer
differently.

What you're asking for is

cli::auto_ptr<FileStream> fs(fi.OpenRead());

which may or may not exist. If it doesn't, you can try to write your
own. It would have a little bit of an overhead, but only a couple of
extra CLI instructions.

Here's an auto pointer template that I wrote, although it has a
completely different purpose (to store unmanaged objects inside a
managed wrapper):

#pragma once

template <class T>
public ref class CliScopedPtr
{
typedef CliScopedPtr<T> this_type;

public:
CliScopedPtr()
: ptr(nullptr)
{
}

explicit CliScopedPtr(T* p)
: ptr(p)
{
}

~CliScopedPtr()
{
delete ptr;
}

T* get()
{
return ptr;
}

T& operator*()
{
return *ptr;
}

T* operator->()
{
return ptr;
}

bool assigned()
{
return ptr != nullptr;
}

void swap(CliScopedPtr% other)
{
T* tmp = other.ptr;
other.ptr = ptr;
ptr = tmp;
}

void swap(CliScopedPtr^ other)
{
T* tmp = other->ptr;
other->ptr = ptr;
ptr = tmp;
}

void reset()
{
this_type().swap(this);
}

void reset(T* p)
{
this_type(p).swap(this);
}

private:
T* ptr;
CliScopedPtr(CliScopedPtr%); // disabled
CliScopedPtr% operator=(CliScopedPtr%); // disabled
};


Credit goes to boost::scoped_ptr for the idea.

Example:

class Unmanaged { };

CliScopedPtr<Unmanaged> ptr(new Unmanaged);

Tom
 
Tamas Demjen said:
Alexei said:
Now, does it matter if the object is created with gcnew or by calling a
function? No, IMO. The same code I can rewrite with using
fi.OpenRead():

{
FileStream ^ fs = fi.OpenRead();
try {
} finally {
delete fs;
}
}

Something like this would be nice, but it can't be done the way you're
trying to do it.
What leads me to simply:

FileStream fs(fi.OpenRead());

I think the code above would expand to

FileStream^ fs = gcnew FileStream(fi.OpenRead());
[...]

and there is no such copy constructor in FileStream (and even if there was
one, you wouldn't want to make a copy in this case).
I can't understand the point of having copy-constructors for ref classes.

There is such a thing as copy c'tor and assignment operator in C++/CLI.
Because what if you want to make a copy? You could always define a static
member (class method) name CopyMe, but C++/CLI was designed to be as
conforming with C++ as possible. The stack syntax with ref classes has to
work like stack objects in native C++, otherwise you're inventing a new
language that has nothing to do with C++. Exactly that happened with the
old MC++, where the destructor syntax was used for the finalizer. Such a
radical change has disadvantages, because C++ programmers have certain
expectations about the language, such as the copy c'tor syntax, even if
it's implemented in the underlying .NET layer differently.

What you're asking for is

cli::auto_ptr<FileStream> fs(fi.OpenRead());

which may or may not exist. If it doesn't, you can try to write your own.
It would have a little bit of an overhead, but only a couple of extra CLI
instructions.

Here's an auto pointer template that I wrote, although it has a completely
different purpose (to store unmanaged objects inside a managed wrapper):

#pragma once

template <class T>
public ref class CliScopedPtr
{
typedef CliScopedPtr<T> this_type;

public:
CliScopedPtr()
: ptr(nullptr)
{
}

explicit CliScopedPtr(T* p)
: ptr(p)
{
}

~CliScopedPtr()
{
delete ptr;
}

T* get()
{
return ptr;
}

T& operator*()
{
return *ptr;
}

T* operator->()
{
return ptr;
}

bool assigned()
{
return ptr != nullptr;
}

void swap(CliScopedPtr% other)
{
T* tmp = other.ptr;
other.ptr = ptr;
ptr = tmp;
}

void swap(CliScopedPtr^ other)
{
T* tmp = other->ptr;
other->ptr = ptr;
ptr = tmp;
}

void reset()
{
this_type().swap(this);
}

void reset(T* p)
{
this_type(p).swap(this);
}

private:
T* ptr;
CliScopedPtr(CliScopedPtr%); // disabled
CliScopedPtr% operator=(CliScopedPtr%); // disabled
};


Credit goes to boost::scoped_ptr for the idea.

Example:

class Unmanaged { };

CliScopedPtr<Unmanaged> ptr(new Unmanaged);

Tom

Guess what, just installed the June CTP of VS2005, and they included exactly
this through the auto_handle template in the msclr namespace.

#include <msclr\auto_handle.h>
....
FileInfo fi("......");
msclr::auto_handle<FileStream> fs(fi.OpenRead());
...

Willy.
 
Willy said:
Guess what, just installed the June CTP of VS2005, and they included exactly
this through the auto_handle template in the msclr namespace.

#include <msclr\auto_handle.h>
...
FileInfo fi("......");
msclr::auto_handle<FileStream> fs(fi.OpenRead());
..

Willy.

Thank you for the information, it's very good to know. It will solve the
original poster's problem.

My CliSmartPtr, however, is slightly different. auto_handle<T> is a
managed smart pointer around T^. CliSmartPtr<T> is a managed smart
pointer around T*. I intended to use it in situations like

ref class C
{
private:
CliSmartPtr<Unmanaged> unmanaged;
[...]
};

It yet has to be tested with a real-world project. :-)

Tom
 
Back
Top