【问题标题】:Mapping of C++ specific features to C++/CLI将 C++ 特定功能映射到 C++/CLI
【发布时间】:2016-07-29 08:46:44
【问题描述】:
我正在用 C++/CLI (VS 2015) 为给定的 C++ 代码编写一个包装器,以便最终使用 C# 中生成的托管 dll。因此,我创建了一个调用本机类的托管类。由于返回 unique_ptr 的工厂方法,此本机类链接到给定的 C++ 代码。
现在我卡住了,因为 C++ 代码有一个回调:
Callback(std::function<void(const Result &>)
结果是类型
std::vector<std::pair<float, float>>
问题:
- 如何在 C++/CLI 中映射回调参数std::function?
我必须在哪里以及如何转换 C++ 结果结构(对向量)
到 .NET 数据结构(元组列表)
就在 C# 中访问它的回调而言?
-
是否可以编写映射到非托管数据结构(向量)的 lambda 表达式(使用 .NET 集合)?
unsigned int cnt = 0;
nativeClass->Callback([&cnt] (const Result &v) {
auto it = d::max_element(v.begin(), v.end(),
[](const Pair &a, const Pair &b) {
return a.second < b.second;
});
// do something with iterator it
// …
cnt++;
});
因此我是否必须编写 lambda 表达式包装器?
【问题讨论】:
标签:
c#
c++
lambda
unmanaged
managed
【解决方案1】:
我会稍微改一下你原来的问题,以更好地适应我提出的解决方案:
- 如何将 C# 方法包装为非托管 C++
std::function<> 以用作回调?
- 如何将回调方法的数据从 C++ (
std::vector<std::pair<>>) 转换为 C# (List<Tuple<>>)?
- 是否可以使用包含捕获变量的 C# lambda 表达式作为回调方法?
请参阅下面的解决方案。请记住,它并不漂亮,并且不会进行任何错误或资源处理。它对上述问题的回答如下(Inner 是您要包装的给定 C++ 类):
- 回调方法可以作为 C#
Action<List<Tuple<>>> 提供(参见Middle::SetCallback)。它被包装方法Middle::WrapCallback 调用。这又可以编组为提供给内部类的非托管函数指针(请参阅构造函数)。
- 我不知道任何集合的编组助手,所以这只是通过复制相应的(假设是只读的)数据结构来完成的(参见
Middle::WrapCallback)。 Middle::DoCallback 只是用来触发外部回调,所以它需要反向进行转换。
- 当然可以,但这完全取决于使用此包装器的 C# 客户端代码。
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
namespace Wrapper {
delegate void CallbackPointer(const Result &data);
typedef void(*UnmanagedPointer)(const Result &data);
Middle::Middle()
{
instance = new Inner();
CallbackPointer^ managed = gcnew CallbackPointer(this, &Middle::WrapCallback);
IntPtr stubPointer = Marshal::GetFunctionPointerForDelegate(managed);
UnmanagedPointer UnmanagedCallback = static_cast<UnmanagedPointer>(stubPointer.ToPointer());
instance->SetCallback(UnmanagedCallback);
}
void Middle::SetCallback(Action<List<Tuple<float, float>^>^>^ callback)
{
handler = callback;
}
void Middle::DoCallback(List<Tuple<float, float>^>^ data)
{
Result *buffer = new Result();
for (int i = 0; i < data->Count; i++)
{
std::pair<float, float> *el = new std::pair<float, float>(data[i]->Item1, data[i]->Item2);
buffer->push_back(*el);
}
instance->Callback(*buffer);
}
void Middle::WrapCallback(const Result &data)
{
List<Tuple<float, float>^>^ buffer = gcnew List<Tuple<float, float>^>();
for (Result::size_type i = 0; i < data.size(); i++)
{
buffer->Add(Tuple::Create(data[i].first, data[i].second));
}
handler->Invoke(buffer);
}
}