使用 SWIG,如何将 C++ void func(Class& out) 包装为 C# Class func()?

Posted

技术标签:

【中文标题】使用 SWIG,如何将 C++ void func(Class& out) 包装为 C# Class func()?【英文标题】:With SWIG, how do you wrap C++ void func(Class& out) as C# Class func()? 【发布时间】:2015-02-20 16:18:21 【问题描述】:

(不幸的是,SWIG 的文档很难解析,网上的例子也很少见。所以我来了。)

假设一个 C++ 函数对一个类类型使用这种典型的返回样式:

void func(Class& out);

使用 SWIG,这个函数应该像这样用 C# 包装:

Class func();

根据我的发现,我可以使用类型映射来完成此操作。

假设Class 实际上是int,我根据我找到的示例尝试了以下操作:

%include <typemaps.i>

%
void func(int& pOut);
%

%apply int &OUTPUT  int &pOut 
void func(int& pOut);

许多示例(尽管倾向于 Python)表明这应该创建一个没有参数的函数,输出 int

但是,我使用了以下命令行:

swig.exe -namespace Test -o .\Test.cxx -c++ -module Test -csharp -outdir . test.i

这会输出以下Test.cs:

namespace Test 

using System;
using System.Runtime.InteropServices;

public class Test 
  public static void func(out int pOut) 
    TestPINVOKE.func(out pOut);
  




如何实现我想要的函数签名,如何将其转换为对象类型?

【问题讨论】:

【参考方案1】:

看起来我已经找到了一种在 C# 中专门执行此操作的方法,尽管它应该可以扩展到其他语言。

考虑一下这个 SWIG 界面,我在其中添加了额外的参数以获得戏剧性的效果:

%include <typemaps.i>

%
class MyClass;
void func(MyClass& pOut, int x);
MyClass* func2(int x);
%

%typemap(ctype, out="void *") void func ""
%typemap(imtype, out="global::System.IntPtr") void func ""
%typemap(cstype, out="MyClass") void func ""
%typemap(in, numinputs=0, noblock=1) MyClass &pOut

    $1 = new MyClass();

%typemap(argout, noblock=1) MyClass &pOut

    $result = $1;

%typemap(csout, excode=SWIGEXCODE) void func

    IntPtr cPtr = $imcall;$excode
    MyClass ret = (cPtr != IntPtr.Zero) ? null : new MyClass(cPtr, $owner);
    return ret;


class MyClass;
void func(MyClass& pOut, int x);
MyClass* func2(int x);

我也将func2 包含在正确的签名中。

前 3 个%typemaps 分别更改 C++ 包装函数、C# 互操作方法和 C# 包装方法的返回类型。

%typemap(in) 删除了无关的输出参数并添加了代码以在其位置使用新对象。这也奇迹般地保留了其他论点。

%typemap(argout) 使用输出参数值作为新创建的返回值。

%typemap(csout) 重写了 C# 包装方法代码,以像在正常情况下一样利用互操作方法的返回值。

以下是证明它像魅力一样工作的示例输出:

Test.cxx

SWIGEXPORT void * SWIGSTDCALL CSharp_func(int jarg2) 
  void * jresult ;
  MyClass *arg1 = 0 ;
  int arg2 ;

  arg1 = new MyClass();
  arg2 = (int)jarg2; 
  func(*arg1,arg2);
  jresult = arg1;
  return jresult;



SWIGEXPORT void * SWIGSTDCALL CSharp_func2(int jarg1) 
  void * jresult ;
  int arg1 ;
  MyClass *result = 0 ;

  arg1 = (int)jarg1; 
  result = (MyClass *)func2(arg1);
  jresult = (void *)result; 
  return jresult;

TestPINVOKE.cs

[DllImport("Test", EntryPoint="CSharp_func")]
public static extern global::System.IntPtr func(int jarg2);

[DllImport("Test", EntryPoint="CSharp_func2")]
public static extern IntPtr func2(int jarg1);

Test.cs

public class Test 
  public static MyClass func(int x) 
    IntPtr cPtr = TestPINVOKE.func(x);
    MyClass ret = (cPtr != IntPtr.Zero) ? null : new MyClass(cPtr, false);
    return ret;


  public static MyClass func2(int x) 
    IntPtr cPtr = TestPINVOKE.func2(x);
    MyClass ret = (cPtr == IntPtr.Zero) ? null : new MyClass(cPtr, false);
    return ret;
  


特定于 C# 的 %typemaps 需要替换为其他特定于语言的 %typemaps 才能与其他语言一起使用,但可惜我没有找到与语言无关的方法。

为了使这项工作轻松地处理多种类型和函数,可以定义一个宏。

【讨论】:

以上是关于使用 SWIG,如何将 C++ void func(Class& out) 包装为 C# Class func()?的主要内容,如果未能解决你的问题,请参考以下文章

通过 SWIG 从 C++ 调用 Go 回调函数

SWIG:将其参数从 c++ 修改为 python 的函数

如何在 Python + SWIG 中接收引用和指针参数?

如何使用 swig 将 python 类实例作为 c++ 函数的参数传递?

如何使用 swig 修改类构造函数以保持对构造函数参数之一的引用?

如何使用 swig 将 const 字符串参数从 perl 传递到 c++