|
I highly recommend getting Nishant Sivakumar's "C++/CLI In Action" book if you're looking for examples. He covers a lot of confusing stuff with remarkable clarity and brevity.
How are you using pFunc ? It's a pointer for the native code to use. If you want to use a native function pointer, you'd use GetDelegateForFunctionPointer() in managed code.
Also, I'm not certain, but I'm concerned about the scope of your variables. When m goes out of the scope of Form1(void) it may be garbage collected.
|
|
|
|
|
Thanks, the book I am working with now is "Pro Visual C++/CLI" by Stephen Fraser, but I will look into the book you recommend too.
I see, I guess I am trying to use pFunc() in the managed code itself. But you know, it actually works if I call pFunc() right at the end of the Form constructor. I get a warning message box that it is unsafe to call function pointers from managed code, but when I click "Continue" the pFunc() is actually called, it does its work and returns properly. However, if I call pFunc() from the Button handler of the Form or from elsewhere the project does not even build:
public:
Form1(void)
{
InitializeComponent();
MyFormMethod^ m = gcnew MyFormMethod(this, &Form1::MyMethod);
IntPtr pint = Marshal::GetFunctionPointerForDelegate (m);
PFUNC pFunc = (PFUNC)pint.ToPointer ();
<code>pFunc();
}
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
<code>pFunc();
}
It's a good point about m garbage collected at some point. Although it should be OK since I only needed it at the time I extracted pointer out of it. Then the pointer is stored in pFunc, which is global and should not get deallocated.
By the way, should we be even talking about calls between managed/unmanaged code here? My test app in my example is just a plain WinFormsApp "Hello World" example (should be all "managed") with a few mods: I put function foo() in my WinFormsApp.CPP with the code to access something on the form (in Form1.h), say a textBox . I press the button to call foo() and foo() writes some text, that's it. I cannot use Form1::textBox::Text = "Text" in foo() , I tried to create a new public method in my Form that does this->textBox->Text = "Text" , but the compiler says "cannot access a non-static member". OK, I make my method static but then I cannot access the textBox from it anymore: error C2597: illegal reference to non-static member 'System::Windows::Forms::Form::Text' .
Of course, I will keep reading and searching for the example that does exactly what I am trying to do, but the solution to my problem should be very simple. Maybe it is so simple that they do not explain it in the books. If someone could just tell me what I am missing...
Thanks!
Eugene
|
|
|
|
|
I'm sorry. I took "This was a simple task before as I just used pointers" to mean that you had existing native C++ code calling the C++ module that does all the work and were moving that existing code into C++/CLI.
In your case, you can define an event to update the text:
delegate void UpdateText( String^ );
ref class SomeClass
{
public:
event UpdateText^ OnUpdateText;
};
public ref class Form1 : Form
{
public:
Form1()
{
InitializeComponent();
someClass_ = gcnew SomeClass();
someClass_->OnUpdateText += gcnew UpdateText( this, &Form1::UpdateTextHandler );
}
private:
SomeClass^ someClass_;
void UpdateTextHandler( String^ text )
{
textBox->Text = text;
}
}; In the implementation of SomeClass , call OnUpdateText( "New text" ) when you want to update the text.
-- modified at 22:59 Monday 13th August, 2007
[Some corrections made to above code.]
|
|
|
|
|
Well, it did not work exactly as was written, but certainly led me to the working code, thank you very much!
However, the solution I finally got seems ugly to me:
<font color=green>
<code>public</code> delegate void UpdateText( String^ );
<font color=green>
<code>public</code> ref class SomeClass
{
public:
event UpdateText^ OnUpdateText;
};
public ref class Form1 : Form
{
public:
Form1()
{
InitializeComponent();
someClass_ = gcnew SomeClass();
someClass_->OnUpdateText += gcnew UpdateText( UpdateTextHandler );
}
private:
SomeClass^ someClass_;
void UpdateTextHandler( String^ text )
{
textBox->Text = text;
}
<font color=green>
System::Void button1_Click (System::Object^ sender, System::EventArgs^ e) {
void foo (String^ Text);
foo ("Text for textBox");
}
};
Now here is MyCode.CPP:
#include "stdafx.h"
#include "Form1.h"
using namespace EventTest;
<font color=green>
<code>public ref class MyClass: public SomeClass
{
public:
void MyClass_UpdateText (String^ Text) {
SomeClass^ EventKeeper = gcnew SomeClass;
EventKeeper->OnUpdateText (Text);
}
};</code>
void foo (String^ Text) {
MyClass^ y = gcnew MyClass();
y->MyClass_UpdateText (Text);
}
Question #1: does it really have to be that twisted-complicated or it is just me being a beginner? To update a property on a Form from outside the Form I need to create and delegate an event, that has to be a member of SomeClass. Then to fire this event we have to create a new MyClass based on SomeClass with the actual event handler code and create an instance of it. And only then we can raise this event. Wow! Did I get it right or there is an extra step here?
Question #2: If SomeClass is declared in front of Form1 then I cannot use the form designer. It says: The class Form1 can be designed, but is not the first class in the file. Visual Studio requires that designers use the first class in the file. Move the class code so that it is the first class in the file and try loading the designer again. But I cannot move it anywhere else because it must go before Form1() constructor as it uses this class, right? Is this something I just have to live with or there is a better way?
Sorry for the lengthy description but as I am getting to the bottom of this issue (it's been 2 days) I'd like other people who search for the same answer can read this thread and see something that actually works. I really appreciate the time spent on helping me, hopefully this will help a few more people!
Thanks again
Eugene
|
|
|
|
|
You are definitely doing too much regarding Question #1. You don't need to create an object every time you need to fire an event. I'm sorry my example code wasn't better. I just typed it into a text editor. I should have grabbed it from a working project.
Regarding Question #2, there's definitely a better way. I just stuck everything into one place for simplicity. I'd only confuse you more if I tried to describe a better way, so I'll make a working example in C++/CLI and send that to you.
-- modified at 22:39 Monday 13th August, 2007
To be a little clearer and more correct, here's the form class with irrelevant stuff stripped out:
#include "WorkerClass.h"
namespace WinApp1
{
public ref class Form1 : public Form
{
public:
Form1()
{
InitializeComponent();
workerClass_ = gcnew WorkerClass();
workerClass_->OnUpdateText += gcnew UpdateText( this, &Form1::UpdateTextHandler );
}
private:
TextBox^ textBox_;
Button^ button_;
WorkerClass^ workerClass_;
void button__Click( System::Object^ sender, System::EventArgs^ e )
{
workerClass_->DoSomething();
}
void UpdateTextHandler( String^ text )
{
textBox_->Text = text;
}
};
} And, in another file called WorkerClass.h, here's WorkerClass , which was SomeClass in my previous example:
namespace WinApp1
{
delegate void UpdateText( String^ );
ref class WorkerClass
{
public:
event UpdateText^ OnUpdateText;
void DoSomething()
{
++serialNumber_;
OnUpdateText( Convert::ToString( serialNumber_ ) );
}
private:
int serialNumber_;
};
} You don't need to derive your class from WorkerClass . All you need is an event of type UpdateText^ to which Form1 can attach a handler.
|
|
|
|
|
Well, this code actually works. And the designer works too, BTW.
You just saved another soul that almost started to hate the new technology
Thanks!
Eugene
|
|
|
|
|
In a VS2005 SP1 C++/CLI project I have a class declared like:
ref class CallbackProxy
{
}; This class is a private member of another C++/CLI class, AsioDevice . I've turned on code analysis in Visual Studio, and I get this:
warning: CA2213 : Microsoft.Usage : 'AsioLib.AsioDevice' contains field 'callbackProxy_' that is of IDisposable type: AsioLib.CallbackProxy. Change AsioLib.AsioDevice's Dispose method to call Dispose or Close on this field. How did my CallbackProxy class become IDisposable ? None of its members are IDisposable .
|
|
|
|
|
|
Yeah, it does. But that link doesn't really clarify things for me. In my class' ctor, I new a pointer to a native struct . In my class' dtor, I delete said pointer. If what I'm reading is correct, the dtor is not called when the reference to callbackProxy_ goes out of scope; I need to explicitly call delete on it. That just seems totally counterintuitive to using managed code.
But what I'd really like is a auto_ptr<> -like class, which I found here: http://www.codeproject.com/managedcpp/CAutoNativePtr.asp
|
|
|
|
|
Hi Michael,
I don't know how it is in C++, this is what I can tell you about C#:
- structs are value types, they get allocated on the stack (in the stack frame
of the method that defines them) or inside an object that holds them (similar
to an int). You can "create" them with the new keyword, but that merely serves
as a way to pass initial values. No one cares about destroying them, they
disappear when the method returns.
- class objects are reference types; you create (i.e. allocate and initialize) them
with the new keyword (that would be gcnew in C++), and they get collected by the
gc if no live objects are referencing them any more.
- there is no such thing as a destructor, but you can have a class implement
the IDisposable interface by providing a Dispose method and stating the
interface explicitly.
- There is no automatic addition of a Dispose method or IDisposable interface (as
opposed to the article about C++).
Hope this helps.
Luc Pattyn [Forum Guidelines] [My Articles]
this weeks tips:
- make Visual display line numbers: Tools/Options/TextEditor/...
- show exceptions with ToString() to see all information
- before you ask a question here, search CodeProject, then Google
|
|
|
|
|
In C++/CLI, both class and struct are reference types. They need to be explicitly modified using the value keyword to get a value type.
My original problem is a design flaw. If I use a smart pointer, I don't need a destructor anymore and the problem goes away.
-- modified at 11:11 Monday 13th August, 2007
Luc Pattyn wrote: there is no such thing as a destructor
Are you sure about that? I made this class:
class Class1
{
public Class1()
{
Console.WriteLine( "Class1 constructed." );
}
~Class1()
{
Console.WriteLine( "Class1 destructed." );
}
}
and I got "Class1 destructed." in my Output window. It was not deterministic, but it was called.
|
|
|
|
|
His post was C# specific, which is quite different from C++/CLI in some ways. In C# there is no destructor. The destructor syntax in C# ( ~Class1() ) is actually a finalizer. In C++/CLI ~Class1() maps to the Dispose() function from IDisposable giving you a form of deterministic destruction (though memory won't be reclaimed immediately you can count on resources being closed). The !Class() syntax maps to the finalizer in C++/CLI.
|
|
|
|
|
Class1 is a C# class. Take up the semantic argument with Microsoft for putting a "Destructors (C# Programming Guide)" page in the MSDN library.
|
|
|
|
|
semantics and documentation arguments aside, did your question get answered?
|
|
|
|
|
The second post in this thread answered the original question. Thanks, Mark.
|
|
|
|
|
oh right, wasn't paying close enough attention. And yes MS semantics are horribly confusing.
In that case, the C# ~Class1() is the finalizer. And by explicitly defining one you ensure that it is called by the garbage collector before the memory for an instance of the class is reclaimed. So although you're printing 'destructed' its actually the garbage collector calling the finalizer before memory is reclaimed. Its not a destructor that runs when the variable goes out of scope as you would get if it was a C++/CLI ref class with ~Class1() defined. And unlike destruction, finalization is nondeterministic and incurs a high performance cost for both allocation and deallocation. So you never want to write one unless your class contains unmanaged resources that won't be cleaned up by the garbage collector. In that case the finalizer will ensure the unmanaged stuff is cleaned up even if someone forgets to call delete or Dispose .
|
|
|
|
|
I don't want to argue, but you're not being specific enough. A C# destructor is more than syntactic sugar. The code within it will be called and then an implicit call is made to Finalize(). I'm not disputing or judging what actually occurs, I'm just pointing out that "C# destructors do not exist" is not a true statement. And I'm certainly not advocating that every C# class define a destructor. I'm glad I made my original mistake in assuming that C++/CLI ctor/dtor behavior is the same as C++ ctor/dtor behavior because it cleared things up after clouding them for awhile.
|
|
|
|
|
That's just it though, it is syntactic sugar. When you write ~Class1() in C# you've got a finalizer. It's not doing something then calling Finalize() I don't believe. Internally the compiler synthesizes that to a call to Dispose(false) which is invoked by the garbage collector right before the memory is reclaimed nondeterministically.
By contrast, a destructor in C++/CLI is synthesized to
Dispose()<br />
{<br />
Dispose(true);<br />
GC::SuppressFinalize();<br />
}
While a call to !Class1() in C++/CLI (the finalizer) results in a call to Dispose(false) .
The ~Class1() is a finalizer, not a destructor. To call it a destructor is a complete misnomer and doesn't convey when or how its called. A destructor would be called automatically when the object goes out of scope (via a using block in C# or via stack semantics in C++/CLI) or when invoked specifically by Dispose() . You must not reference other finalizable objects in that finalizer and other similar rules for finalizers that aren't present in a destructor. In C++/CLI a class destructor is created by the compiler automatically and will clean up managed resources for you. A finalizer (~Class1() in C#) is not generated by the compiler. It only exists if you explicitly write one. Most important is the fact that the ~Class1() in C# is nondeterminitic which by its nature makes it not a destructor (since they're deterministic) and the performance penalty for creating a finalizer for a class (In C++/CLI you would incur this cost with !Class1() but ~Class1() would enable actual RAII destructor semantics like native C++).
In C++/CLI you would write ~Class1() to properly handle the closing of resources in a way that can be done deterministically (when the object goes out of scope or a call to delete is made). This is a destructor. In C# when you write ~Class1() you're providing a way for the garbage collector to handle resource cleanup or unmanaged cleanup at the last second before memory reclamation in the case that the user failed to manually release it with Dispose or a using block. This is a finalizer. There is no destructor. You can't call delete. You have to write a Dispose method and/or use using blocks.
The C++/CLI dtor/ctor behavior is semantically the same as native C++. Although the destructor in C++/CLI doesn't cause the memory to actually be reclaimed (as that's always the garbage collectors job for managed objects) it is invoked deterministically (unlike the C# ~Class1() finalizer) which ensures resources will be closed immediately (unlike the C# finalizer).
Hopefully that wasn't too rambling...
|
|
|
|
|
From the source code perspective, I have code in a destructor that gets called. That's it. I'm not concerned with what goes on underneath. We're debating semantics. When you write ~Class1() in C#, you've got a destructor, both in name and behavior.
In native C++, one would expect a destructor to be called implicitly when an automatic variable goes out of scope. Such is not the case in C++/CLI. What's defined as a destructor in C++/CLI does not get called when an automatic variable goes out of scope. What's defined as a finalizer in C++/CLI gets called when an automatic variable goes out of scope. That, to me, is a big difference.
|
|
|
|
|
You seem to be confused.
You have to be concerned with what goes on underneath or you won't understand how this works. When you write ~Class1() in C# you have a finalizer in behavior. The syntax is like a C++ destructor and this is confusing on Microsoft's part. Assume you have a C# class with ~Class1() defined. Now, whenever you instantiate an object of Class1 you incur a performance penalty simply because the finalizer exists. The Class1 instance must register in a Finalizable queue and an freachable queue when the GC does its business. ~Class1() (really a call to Dispose(false) ) will be invoked by the garbage collector every time an object of Class1 is about to be reclaimed. The simple existence of ~Class1() and the subsequent placement in the freachable queue can cause objects of Class1 to be promoted to another generation and not be collected by the GC when it really could be. Most importantly, you have no idea when ~Class1() will be called by the garbage collector. Its completely nondeterministic. This by its very nature means its not a destructor. The finalizer is there to let you code last minute cleanup of unmanaged resources (which the GC cannot work with) in case the user of your class did not properly call Dispose() on an instance of Class1 or use a using block. And like I said before, there's a huge number of restrictions for what you can do in a finalizer vs a destructor.
Now assume we have Class1 in C++/CLI with ~Class1() defined. This will be called when an object of Class1 goes out of scope IF you declared that object with stack semantics (i.e., Class1 myClass1Object ). If you dynamically allocate memory with gcnew then you must use delete to invoke the destructor deterministically. This is the same as what happens in native C++. The difference in C++/CLI is that the memory itself won't be immediately reclaimed immediately (as that's the GC's job and it works nondeterministically) but that's not an issue. It will be reclaimed when more memory is required. The important part is that the destructor (~Class1() ) will always be invoked implicitly when the automatic variable goes out of scope. A C# class with ~Class1() defined does not function this way because in C# ~Class1() is a finalizer not a destructor. So in C++/CLI you put resource-release code in your destructor ~Class1() and then an automatic variable (such as a FileStream object) will have the destructor implicitly called and close the stream for you so there is no resource leak. In C# your finalizer ( ~Class1() ) will not be called implicitly when the object goes out of scope. It will be called sometime later by the GC nondeterministically. This can lead to errors when you think a resource has been released but it hasn't because the GC hasn't invoked the finalizer yet. Hence the need for using blocks in C# when C++/CLI can simply use stack semantics because of deterministic destruction via destructors (~Class1() ).
In C++/CLI a finalizer is declared with !Class1() as opposed to C# using ~Class1(). The same rules for finalizers is true in C++/CLI as C# (as outlined in my second paragrah).
|
|
|
|
|
I may be reading your comments wrong, but did you state a C++ ref class destructor is a finalizer?
That's not the case. The destructor maps to Dispose() and a finalizer must be explicitly added.
Destructors do not get called if you allocate an object on the managed heap and never explicitly
delete it. The finalizer, however will be called when the handle no longer has any references to it
and the GC gets around to cleaning it up. That's why native objects should be freed in a finalizer,
not a destructor (although the destructor should call the finalizer - the compiler takes care of
SuppressFinalize).
Having to explicitly delete objects would be counterintuitive, as Michael originally stated.
Being able to (deterministically) using the familiar C++ syntax, however, is a nice, powerful
feature.
Note I'm only referring to C++ - I'll make no comments about C# except for the related Dispose/Finalize
pattern of CLR.
Cheers,
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
I'm pretty sure we've said the same thing. However...
You say destructors are not called on an object allocated on the managed heap which isn't strictly true. If you allocate it on the managed heap with gcnew then yes the destructor is never called if you don't manually call delete . However, if you instantiate the ref class object with stack semantics then the destructor is called implicitly when the object goes out of scope (even though underneath its still allocated on the managed heap like all ref class objects). This is the critical addition to C++/CLI (vs MC++) that allowed for RAII style coding like native C++. Michael was claiming this is not the case (as it is in native C++) and that the finalizer is what is called, which is not correct. The finalizer will be called eventually either way if you define one, but the way things work is obviously much different than what a destructor provides.
The confusion is that in C# when you declare ~Class1() this is actually mapped to the finalizer (Dispose(false) ) in the dispose pattern. C# has no concept of destructors like C++ or C++/CLI even though the semantics there look like you're declaring a destructor. In C++/CLI you accomplish the same thing by declaring !Class1() which more properly distinguishes the finalizer from the destructor. Then declaring ~Class1() in C++/CLI does a very different thing than ~Class1() in C#. That's why in C# you need to explicitly call Dispose() in a finally block or use a using block with things like FileWriter objects to ensure there is no resource leak. C++/CLI's destructor semantics provide the handy native C++ style, FileWriter fw; which will automatically be disposed of properly when it goes out of scope (deterministically).
|
|
|
|
|
iddqd515 wrote: in C# when you declare ~Class1() this is actually mapped to the finalizer (Dispose(false)) in the dispose pattern.
Dispose() isn't the finalizer...Finalize() is, right?
Regardless, C++ does the same - the destructor becomes Dispose() (quite literally,
as Michael's original warning message stated).
In C++ the Destructor is explicitly called when a stack-based syntax managed object
goes out of scope, the destructor is called directly, a managed heap object
is explicitly deleted with delete, or another language calls Dispose() on an object.
If the class implements a finalizer then that needs to be (at least SHOULD be) called from
the destructor. The compiler takes care of calling SuppressFinalize so the finalizer will not
be called again by the GC.
Isn't that the same as the C# (and probably VB) Dispose/Finalize pattern?
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Are you being purposefully daft? I understand what's going on; I'm arguing that "C# does not have destructors" is an untrue statement. I'm not repudiating the mechanics.
Dispute this: The C# language specification allows for the definition of destructors for class types.
|
|
|
|
|
From: http://en.wikipedia.org/wiki/Destructor_%28computer_science%29
In object-oriented programming, a destructor (sometimes shortened to dtor) is a method which is automatically invoked when the object is destroyed. Its main purpose is to clean up and to free the resources which were acquired by the object along its life cycle and unlink it from other objects or resources invalidating any references in the process. The use of destructors is key to the concept of RAII.
How in C# do you declare such a thing? A finalizer is not automatically invoked when the object is destroyed deterministically. It is nondeterministically called by the GC at some point in the future. Most important, in no way does a finalizer enable the RAII pattern.
Take a FileStream class which encapsulates a resource in C#. Declare a finalizer for it. When an instance of a FileStream class goes out of the scope the finalizer is not called then. You do not know when it is called. Furthermore, even if the GC does a collect and there are no roots in the code to the object, the simple existence of the finalizer counts as a root and can cause the FileStream object to be promoted into the next generation and not collected by the GC until the next collect cycle (who knows when that will be). So while your FileStream object may have gone out of scope ages ago, by not calling Dispose and assuming the finalizer will close the FileStream , you have potentially caused a resource leak. The next function call that assumes the file you previously had used with the FileStream object is closed may throw an exception because you were relying on the garbage collector to nondeterministically release a critical resource.
On the other hand, a destructor in C++/CLI will be called the moment the FileStream goes out of scope (assuming you declared the object with stack semantics and not gcnew ). Then in the destructor you make sure there is a call to Dispose and you ensure that the resource is released immediately. You don't need a try/finally and you don't need to explicitly call Dispose and nor does anyone who uses your class in the future. The destructor incurs no performance penalty during allocation or deallocation like a finalizer does either. There's no way to declare such a function in C#.
To say 'I'm not repudiating the mechanics' therefore C# has destructors is completely wrong. The mechanics are exactly what separate a finalizer and a destructor. You can't just say 'oh it has a ~Class() declaration therefore its a destructor even if it works completely differently.' They work in fundamentally different ways. Start declaring finalizers for all your C# classes as if it was a C++ destructor and watch the programs performance and reliability plummet.
|
|
|
|
|