本机的回调一般借助函数指针,managed中的回调一般用委托实现。
在混合编程中要用到两者的转换。
其中,重要的是委托到函数指针的转换。
例如,有native中的class定义如下:
typedef void(*nativePrinter_t)(char);
class enumchars
{
std::vector<char> chars;
public:
enumchars(std::vector<char> const& data) : chars(data){}
void launch()
{
if (m_printer)
{
for(auto cur : chars) { m_printer(cur); };
}
}
nativePrinter_t m_printer = nullptr;
};
//global native function
void concreteNativePrinter(char c)
{
std::cout << c << "-";
}
仅仅是打印文本,测试如下:
int main(array<System::String ^> ^args)
{
enumchars ca({'h','e','l','l','o'});
ca.m_printer = concreteNativePrinter;
ca.launch();
return 0;
}
输出:
h-e-l-l-o-
如果想在包装成managed class,则定义如下,包装enumchars指针:
ref class mclass
{
enumchars* ptr;
public:
delegate void printstring(char); //委托的声明中的参数与返回值与函数指针一致
printstring^ managedPrinter = nullptr;
mclass()
{
ptr = new enumchars({ 'h','e','l','l','o' }); //初始化指针
}
~mclass() { delete ptr; } //删除指针
!mclass() { ~mclass(); }
void launch()
{
pin_ptr<printstring^> pin = &managedPrinter;
auto pF = (nativePrinter_t)Marshal::GetFunctionPointerForDelegate(managedPrinter).ToPointer();
ptr->m_printer = pF;
ptr->launch();
}
static void printchar(char c)
{
System::Console::Write(gcnew System::String(&c));
}
};
ref struct managedPrintStr
{
static void printer(char c)
{
System::Console::Write("{0}-"gcnew System::String(&c));
}
};
int main(array<System::String ^> ^args)
{
mclass^ c = gcnew mclass;
c->managedPrinter += gcnew mclass::printstring(mclass::printchar);
c->managedPrinter += gcnew mclass::printstring(managedPrintStr::printer);
c->launch();
return 0;
}
在launch中:
void launch()
{
pin_ptr<printstring^> pin = &managedPrinter;
auto pF = (nativePrinter_t)Marshal::GetFunctionPointerForDelegate(managedPrinter).ToPointer();
ptr->m_printer = pF;
ptr->launch();
}
可以看到Marshal::GetFunctionPointerForDelegate
将委托转换为指针。再强转为native函数指针。因为委托为句柄,要先进行固定,防止GC的压缩移动。
输出:
hh-ee-ll-ll-oo-
注意,多委托可以转为一个函数指针。C++/CLI里的委托类型都是多委托类型。
而Marshal::GetDelegateForFunctionPointer()
则略显多余,因为委托可以直接通过gcnew
创建,参数里为本机函数即可。
网友评论