我正在包装一个 C++ 对象,它有某些按值返回的方法,我希望 C# 包装器方法返回本机类型,而不必在 C++ 领域分配和取消分配内存。在这种情况下,我认为将数据复制到返回类型是合适的,但我不知道如何以一种好的方式做到这一点。
作为一个简单的工作示例,以下 C++ 代码是一个文件解析器。我只列出了 std::pair<int,int> version()
方法,但您可以想象其他方法返回一个小的常量 std::array、list 等。
#include <utility>
#include <tuple>
class MyFile {
public:
std::pair<int,int> version() const noexcept {
return {version_major_, version_minor_};
}
private:
int version_major_;
int version_minor_;
};
我最喜欢这个,但我终其一生都无法弄清楚如何删除只是 C# 包装器的输出参数! C# 负责内存,只是将它传递给 C++ 来填充。这需要一些使用 %extend
的 C++ 包装器,但没关系。
我真的想要一个带有 public Tuple<int, int> version()
签名的最终 C# 方法
%module parser
%include "typemaps.i"
%rename("$ignore", fullname=1) MyFile::version() const noexcept;
%extend MyFile {
void version(int* major_out, int* minor_out) {
std::tie(*major_out, *minor_out) = self->version();
}
}
%typemap(ctype, out="void *") int* major_out, int* minor_out "int *"
%typemap(imtype, out="void") int* major_out, int* minor_out "out int"
%typemap(cstype) int* major_out, int* minor_out "out int"
%typemap(cstype) void MyFile::version "Tuple<int, int>"
%typemap(csin, pre=" int tmp_$csinput = 0;") int* major_out, int* minor_out "out tmp_$csinput"
// implment the C# function that returns the type specified above
%typemap(csout, noblock=1, excode=SWIGEXCODE) void MyFile::version
{
$imcall;$excode
major_out = tmp_major_out;
minor_out = tmp_minor_out;
return new Tuple<int, int>(tmp_major_out, tmp_minor_out);
}
SWIGEXPORT void SWIGSTDCALL CSharp_parser_MyFile_version(void * jarg1, int * jarg2, int * jarg3) {
MyFile *arg1 = (MyFile *) 0 ;
int *arg2 = (int *) 0 ;
int *arg3 = (int *) 0 ;
arg1 = (MyFile *)jarg1;
arg2 = (int *)jarg2;
arg3 = (int *)jarg3;
MyFile_version(arg1,arg2,arg3);
}
[global::System.Runtime.InteropServices.DllImport("parser", EntryPoint="CSharp_parser_MyFile_version")]
public static extern void MyFile_version(global::System.Runtime.InteropServices.HandleRef jarg1, out int jarg2, out int jarg3);
public Tuple<int, int> version(out int major_out, out int minor_out) {
int tmp_major_out = 0;
int tmp_minor_out = 0;
parserPINVOKE.MyFile_version(swigCPtr, out tmp_major_out, out tmp_minor_out);
major_out = tmp_major_out;
minor_out = tmp_minor_out;
return new Tuple<int, int>(tmp_major_out, tmp_minor_out);
}
我尝试了其他方法,但为了保持问题简短,我不会在此处发布所有生成的代码。
这行得通,但我想以这种方式工作的每个方法都需要静态数据。这不成比例。如果我尝试使用成员数据来执行此操作,我很容易遇到生命周期问题,我认为这些数据的生命周期足以让 C# 复制。
csout
类型映射中这也有效,但为一个只返回两个整数的函数分配和取消分配很多!您最终会得到一个 C# 包装器,例如:
public Tuple<int, int> version() {
System.IntPtr cPtr = parserPINVOKE.MyFile_version(swigCPtr);
pair_of_int tmp = new pair_of_int(cPtr, false);
Tuple<int, int> ret = new Tuple<int, int>(tmp.first, tmp.second);
tmp.Dispose();
return ret;
}
一个类似的问题是 this one,但它涉及 java。 Java 有 JNI,Pyhton 有一个 C 接口,可以让你在 C 代码中创建内存/对象并返回它......我没有看到在没有 C++/CLI 的 .NET 中做到这一点的方法(SWIG 没有使用).