// IFoo.cs
public interface IFoo
{
string Bar(int arg1, string arg2);
}
// FooBase.cs
public abstract class FooBase : IFoo
{
public virtual string Bar(int arg1, string arg2)
{
return arg1.ToString() + arg2;
}
}
Now C++/CLI code. First make Native C++ class - NativeFoo - that suit whatever need. Then define managed class Foo that sit between C++ and C# code. Note how the Foo::Bar() convert C# String to C++ std::string and vice versa.
// Foo.hpp
#include
class NativeFoo
{
public:
std::string Bar(int arg1, const std::string& arg2)
{
return arg2 + std::to_string(arg1);
}
};
public ref class Foo : public FooBase
{
private:
NativeFoo* m_Native;
public:
Foo()
: m_Native( new NativeFoo )
{}
~Foo()
{
delete m_Native;
}
String^ Bar(int arg1, String^ arg2) override
{
std::string sarg2 = msclr::interop::marshal_as< std::string >(arg2);
std::string bared = m_Native->Bar(arg1, sarg2);
String^ ret = msclr::interop::marshal_as< String^ >(bared);
return ret;
}
};
Then the managed Foo can be created and Bar() can be used like any C# class.
IFoo f = new Foo();
string ret = f.Bar(42, "The answer is ");
Now callback from C++ to C#. First define callback in C# using delegate - OnFooFn.
public delegate void OnFooFn(IFoo foo, string msg);
public interface IFoo
{
OnFooFn OnFoo
{
get;
set;
}
}
Then FooBase has a helper to call callback - FireOnFoo()
public abstract class FooBase : IFoo
{
...
public OnFooFn OnFoo { get; set; }
public void FireOnFoo( string msg)
{
if (OnFoo != null)
{
OnFoo( this, msg);
}
}
}
Now native C++ class has a callback fn that will callback whatever call Bar().
class NativeFoo
{
public:
std::string Bar(int arg1, const std::string& arg2)
{
m_FooFn("What is the question ?");
return arg2 + std::to_string(arg1);
}
using CallbackFooFn = std::function< void(std::string) >;
void SetCallbackFoo(CallbackFooFn fn) { m_FooFn = fn; }
private:
CallbackFooFn m_FooFn = [](const std::string& arg) {}; // default dummy callback
};
Managed class can't set the above callback directly. It needs a bridge - CallbackBridgeFoo. Using this class, the C++ callback can be made through C#.
class CallbackBridgeFoo
{
private:
NativeFoo* m_Native;
gcroot< FooBase^ > m_CLR; // FooBase has FireOnFoo()
public:
CallbackBridgeFoo(NativeFoo* native, FooBase^ clr)
{
m_Native = native;
m_CLR = clr;
m_Native->SetCallbackFoo([this](const std::string& msg)
{
String^ smsg = msclr::interop::marshal_as< String^ >(msg);
this->m_CLR->FireOnFoo(smsg);
});
}
};
public ref class Foo : public FooBase
{
private:
NativeFoo* m_Native;
CallbackBridgeFoo* m_Bridge;
public:
Foo()
: m_Native( new NativeFoo )
{
m_Bridge = new CallbackBridgeFoo(m_Native, this);
}
~Foo()
{
delete m_Bridge;
delete m_Native;
}
...
};
Finally, C# can enjoy the C++ callback.
IFoo f = new Foo();
f.OnFoo = (sender, msg) => { Console.WriteLine(msg); };
string ret = f.Bar(42, "The answer is ");
No comments:
Post a Comment