游戏开发者联盟

unity通过swig调用c++

c#调用c/c++一般用pinvoke。pinvoke有一套很复杂的规则,也没那么容易掌握。而swig是一个代码生成工具,能生成pinvoke相关的胶水代码。换句话说,就是能根据一定配置,让我们省去写pinvoke代码的过程,而且swig所做的又更进一步,还能生成包装类。

第一次使用swig,做个备忘录。

1. 官方文档:

http://www.swig.org/Doc4.0/SWIGDocumentation.html#CSharp_introduction

2. swig命令

swig -csharp -help 
-dllimport <dl>	 指定dll名称
-namespace com.bloggs.widget 指定命名空间
-outfile <file> 把所有代码写到单个文件

3. Directors

就是这个功能,可以把c++类包装成c#代理类,可以极大减少胶水代码量。

// file: example.h
//官方例子,定义了一个基类,一个Caller类。用Caller类调用基类里的虚函数UIntMethod。
//生成c#代理之后,c#可以继承这个基类,并能覆盖虚函数UIntMethod。
class Base {
public:
  virtual ~Base() {}

  virtual unsigned int UIntMethod(unsigned int x) {
    std::cout << "Base - UIntMethod(" << x << ")" << std::endl;
    return x;
  }
  virtual void BaseBoolMethod(const Base &b, bool flag) {}
};

class Caller {
public:
  Caller(): m_base(0) {}
  ~Caller() { delBase(); }
  void set(Base *b) { delBase(); m_base = b; }
  void reset() { m_base = 0; }
  unsigned int UIntMethodCall(unsigned int x) { return m_base->UIntMethod(x); }

private:
  Base *m_base;
  void delBase() { delete m_base; m_base = 0; }
};

Directors功能默认是关闭的,可以随时在配置文件中打开。执行以下配置,就会生成Base和Caller的代理类。

/* File : example.i */
%module(directors="1") example
%{
#include "example.h"
%}

%feature("director") Base;

%include "example.h"

在c#中继承Base类:

public class CSharpDerived : Base
{
  //覆盖了UIntMethod,如果用caller来调用的话,应该调用这个函数
  public override uint UIntMethod(uint x)
  {
    Console.WriteLine("CSharpDerived - UIntMethod({0})", x);
    return x;
  }
}

写个测试程序,验证以上代码:

public class runme
{
  static void Main() 
  {
    Caller myCaller = new Caller();

    // Test pure C++ class,这里是直接调用的Base类
    using (Base myBase = new Base())
    {
      makeCalls(myCaller, myBase);
    }

    // Test director / C# derived class,这里调用的是c#写的子类
    using (Base myBase = new CSharpDerived())
    {
      makeCalls(myCaller, myBase);
    }
  }

  static void makeCalls(Caller myCaller, Base myBase)
  {
    myCaller.set(myBase);
    myCaller.UIntMethodCall(123);
    myCaller.reset();
  }
}

输出结果

Base - UIntMethod(123)
CSharpDerived - UIntMethod(123)

通常我们不会去继承c++类,而是直接创建实例使用。也不会从c++调用c#(如果真的需要,一般也可以变通反转一下),而是c#去调用c++,这样可以保证简单,不会出错。

4. cmake编译

cxx工程编译使用的是cmake,所以这里也希望用cmake来执行swig。

# ==== SWIG building ====
FIND_PACKAGE(SWIG REQUIRED)
INCLUDE(${SWIG_USE_FILE})
SET(CMAKE_SWIG_FLAGS "")
SET_SOURCE_FILES_PROPERTIES(cexport.i PROPERTIES CPLUSPLUS ON)
#以下指定c#代码的明明空间为ezg,DllImport的dll名字为xxx_cs
set_property(SOURCE cexport.i PROPERTY COMPILE_OPTIONS
  -namespace ezg
  -dllimport ${LIB_NAME}_cs)
# 指定生成xxx_cs.dll的库文件,只添加了一个idl文件。
# 注意,我这里生成的dll只包括自动生成的c胶水代码,不包括实际的库代码。
# 实际库代码在${LIB_NAME}中。
swig_add_library(${LIB_NAME}_cs
  TYPE SHARED
  LANGUAGE csharp
  SOURCES	cexport.i
)
set_target_properties(${LIB_NAME}_cs  PROPERTIES
  SWIG_INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR}
  SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON
  POSITION_INDEPENDENT_CODE ON)
#把实际库代码链接到胶水库xxx_cs。
TARGET_LINK_LIBRARIES(${LIB_NAME}_cs PRIVATE ${LIB_NAME})