从 SWIG

时间:2021-07-13 21:49:16

标签: c# c++ swig

我正在包装一个 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# 方法

SWIG 代码

%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);
}

生成的 C++ 包装器

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);
}

生成的 PInvoke 代码

[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);

生成的 C# 绑定

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;
}

其他 SO 问题

一个类似的问题是 this one,但它涉及 java。 Java 有 JNI,Pyhton 有一个 C 接口,可以让你在 C 代码中创建内存/对象并返回它......我没有看到在没有 C++/CLI 的 .NET 中做到这一点的方法(SWIG 没有使用).

0 个答案:

没有答案
相关问题