这是基于我在C++/CLI中实现CartoType C++地图渲染库包装器的经验,另一种方法来完成它。这是测试过并且可以正常工作的代码。
C++ API有一个异步查找函数,它带有回调函数:
TResult CartoType::CFramework::FindAsync(FindAsyncCallBack aCallBack,const TFindParam& aFindParam,bool aOverride = false);
回调函数的类型如下所示:
using FindAsyncCallBack = std::function<void(std::unique_ptr<CMapObjectArray> aMapObjectArray)>
任务是通过向现有包装系统添加C++/CLI代码来提供.NET包装器。首先,我为我的.NET函数定义了一个合适的委托类型(相当于C++ API中的FindAsyncCallback)。
public delegate void FindAsyncDelegate(MapObjectList^ aMapObjectList);
“.NET 函数的签名如下:”
Result FindAsync(FindAsyncDelegate^ aDelegate,FindParam^ aFindParam,bool aOverride);
主要实现问题需要解决的是如何调用本地C++函数,并提供一个本地回调函数,该函数可以调用由.NET函数的调用者传递的委托。相关任务是保持委托和本地回调函数对象在异步函数的线程完成其工作之前保持活动状态。以下是实现方法。
我定义了一个与C++回调函数类型相同的C++/CLI委托类型,以及一个用于保存调用者传递给.NET函数的委托(FindAsyncDelegate类型)的类,和一个将传递给C++的委托类型(NativeAsyncHandler)。
delegate void NativeAsyncHandler(std::unique_ptr<CMapObjectArray> aMapObjectArray);
ref class FindAsyncHelper
void Handler(std::unique_ptr<CMapObjectArray> aMapObjectArray)
Framework^ m_framework;
FindAsyncDelegate^ m_delegate;
NativeAsyncHandler^ m_native_handler;
};
这个想法是我们创建一个带有两个委托的FindAsyncHelper对象,然后使用本地委托调用本地的FindAsync函数,安排调用Handler(),然后再调用原始调用者的委托。
以下是其实现方式:
typedef void(*FIND_ASYNC_CALLBACK)(std::unique_ptr<CMapObjectArray> aMapObjectArray);
Result Framework::FindAsync(FindAsyncDelegate^ aDelegate,FindParam^ aFindParam,bool aOverride)
{
if (aDelegate == nullptr || aFindParam == nullptr)
return Result::ErrorInvalidArgument;
TFindParam param;
SetFindParam(param,aFindParam);
FindAsyncHelper^ h = gcnew FindAsyncHelper(this,aDelegate);
h->m_native_handler = gcnew NativeAsyncHandler(h,&FindAsyncHelper::Handler);
IntPtr p = Marshal::GetFunctionPointerForDelegate(h->m_native_handler);
FIND_ASYNC_CALLBACK f = static_cast<FIND_ASYNC_CALLBACK>(p.ToPointer());
TResult error = m_framework->FindAsync(f,param,aOverride);
// Keep h alive by adding it to a list.
m_find_async_helper_list->Add(h);
return (Result)(int)error;
}
一些注释:
这些语句
FindAsyncHelper^ h = gcnew FindAsyncHelper(this,aDelegate);
h->m_native_handler = gcnew NativeAsyncHandler(h,&FindAsyncHelper::Handler);
创建一个FindAsyncHandler对象并在其中存储一个本地处理程序对象;将其保留在此处意味着我们只需要保持一个对象的活动状态,即FindAsyncHandler。下一条语句:
IntPtr p = Marshal::GetFunctionPointerForDelegate(h->m_native_handler);
FIND_ASYNC_CALLBACK f = static_cast<FIND_ASYNC_CALLBACK>(p.ToPointer());
获取一个函数指针,可以传递给本地代码,并将其转换为正确的函数指针类型。我们不能直接将其转换为FindAsyncCallback中使用的std :: function类型,因此需要繁琐的额外typedef。
最后可以调用本地的FindAsync函数:
TResult error = m_framework->FindAsync(f,param,aOverride)
接下来,为了确保各种回调函数能够继续运行,FindAsyncHandler被添加到由主框架对象拥有的列表中:
m_find_async_helper_list->Add(h);
当任务完成并调用FindAsyncHelper::Handler时,它将从列表中移除。