Gerhard said:
Ideally, I would also use inheritance in C# to overwrite the virtual
functions.
Will has already explained how you encapsulate native classes inside
managed wrappers. However, your override requirement is tricky to implement.
For example, consider the following sample:
class Native
{
public:
void ExecuteAll()
{
for(int i = 0; i < 5; ++i)
OnItem(i);
}
virtual void OnItem(int i) { }
};
If you want to make OnItem overridable in the managed wrapper, you have
to do some extra work:
ref class Managed; // forward declare
class NativeProxy : public Native
{
public:
NativeProxy(Managed^ managed_source) : managed(managed_source) { }
protected:
virtual void OnItem(int i);
private:
gcroot<Managed^> managed;
};
public ref class Managed
{
public:
Managed() : native(new NativeProxy(this)) { }
~Managed() { delete native; }
protected:
!Managed() { delete native; }
public:
void ExecuteAll() { native->ExecuteAll(); }
protected:
virtual void OnItem(int i) { }
private:
NativeProxy* native;
};
void NativeProxy::OnItem(int i) { managed->OnItem(i); }
This looks pretty straightforward: I simply set up a mechanism that
redirects Native::OnItem to Managed OnItem, so it can be overriden
later. The only problem is that it doesn't compile:
'Managed::OnItem': candidate function(s) not accessible
Unfortuantely I can't declare NativeProxy a friend of Managed, it's
illegal in C++/CLI (don't quite understand why). Fortunately .NET has a
special access modifier called public protected, which means public from
the current unit and protected from everywhere else. With the following
modification, it works fine:
public ref class Managed
{
[...]
public protected:
virtual void OnItem(int i) { }
[...]
};
This eliminates the need of declaring the proxy a friend.
This brings to another big problem. I saw that your virtual function is
declared public, so I intentionally did the same and declared
Native::OnItem public, which is a bad design in this case, as it should
have been protected. That being public greatly complicates matters,
because Native::OnItem can be called publically and internally as well.
I intentionally did not declare Managed::OnItem public, because that
would break the entire wrapper.
If your expectation is that your virtual function can be called
publically, you can't simply make Managed::OnItem public. The following
would cause an infinite recursion:
ref class Managed
{
[...]
public:
virtual void OnItem(int i) { native->OnItem(i); } // stack overflow!
[...]
};
What happens here is that Managed::OnItem calls NativeProxy::OnItem,
which in turn calls Managed::OnItem, and it goes on until you run out of
memory.
If you insist that your virtual function must be available publically,
you really have to name that function differently, and preferably
declare it non-virtual:
ref class Managed
{
[...]
public:
void PublicOnItem(int i) { native->OnItem(i); } // non-virtual
[...]
};
To test this class at runtime, I implemented OnItem:
void Managed::OnItem(int i) { Console::WriteLine(i); }
and wrote a command line app around it:
int main(array<String ^> ^args)
{
Managed m;
m.ExecuteAll();
}
It should display on the console window
0
1
2
3
4
Final note: First I was trying to use auto_gcroot in the NativeProxy,
but it crashed with stack overflow. It seems like Managed dtor called
auto_gcroot dtor, which in turn called Managed dtor again. This is one
of those cases when gcroot should be used, not auto_gcroot (I'm still
confused when to use which).
Hope this helps, not sure if anyone has anything to add to it. I just
wanted to show a complete example to the original poster (and it was a
great excercise to myself as well).
Tom