SWIG基本介绍
C++到其他语言的包装器,本质上还是与其他高级语言使用传统方式调用C++代码的方式相同(如C#的Pinvoke,JAVA的NDK等),而SWIG只是帮助我们简化这个流程,不用为每个类每一个接口手写封装代码(SWIG不是做代码的转换,而是调用封装)。
基本流程:SWIG将C++代码转换为各个语言可以调用的C代码(生成CXX文件,这个文件的C代码内部调用C++的接口),将CXX代码编译成DLL库。之后封装成各自语言调用C++的C#代码(这部分代码生成的是工程名_PINVOKE.cs
命名的C#文件),最后将类封装成C#的代理类.
基本使用
- 编写脚本文件(即告诉C++的哪些类、哪些接口、使用什么转换规则来封装C++的一个脚本文件)
//脚本文件 example.i
%module(directors="1") exmaple
%{
#include "aaa.h" //C++的cxx代码所需要包含的头文件,
%}
%include "aaa.h" //需要将C++的接口文件封装到C#类中,如果这个头文件内部有多个类,对应生成多个C#的类文件.
//如果使用 %import "aaa.h" 那么代表是从另一个SWIG接口文件或头文件中收集某些信息,而不实际生成任何包装器代码。
//执行脚本文件;
swig.exe -c++ -csharp example.i
//执行之后生成的文件有:
aaa.cs exmaple.cxx exmaple_pinvoke.cs 文件
类封装
class Example
{
public:
int add(int x,int y)
{
return x+y;
}
};
常用的类的封装修改说明
## 修改类名
%rename(Simple) Example //修改C#的封装类类名
## 修改类的访问类型
%typemap(csclassmodifiers) SWIGTYPE "protected class"
## 修改构造函数的访问方式
SWIG_CSBODY_PROXY(public, public, Example) //默认在其他模块中可能不能继承,有些构造函数是保护类型
## 修改C#中方法的访问方式
%csmethodmodifiers Example::add "protected"
## 给C++类中添加扩展方法
%extend //$self 如果需要访问内部的成员变量,需要将变量通过公有方法来访问,$self是当前对象的指针,不能使用this代替;
{
bool sub(int x,int y)
{
return x-y;
}
}
//添加的方式是目标代码,内部代码是C#代码,这个是与%extend的区分
%proxycode %{
public String toString() {
boolean flag = FetchFlag();
return Boolean.toString(flag);
}
%}
## 给C#代理类增加成员变量和C#方法
%typemap(cscode) Example %{
private int const_value=5;
public void plus(int x,int y) {
return x+y-const_value;
}
%}
## 修改整个代理类(需要注意,如果有多个地方修改了代理类,只使用最后一个)
//typemap(csbody) 这个是非继承类 typemap(csbody_derived) 这个是继承类,对于模板类修改这个代理类的方式也是生效;
%typemap(csbody_deviced) Example %{
private global::System.Runtime.InteropServices.HandleRef swigCPtr;
protected bool swigCMemOwn;
public $csclassname(global::System.IntPtr cPtr, bool cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
}
}
## 添加一个C++不存在的类
%pragma(csharp) imclassimports=%{
using System.Reflection;
using System.IO;
using System.Collections;
using System;
using System.Collections.Generic;
public class SwigAssembly
{
}
%}
## 将一个类设置为回调类,可以由其他语言继承,dirprot是否对包含的方法继承;
%feature("director","dirprot"="1") Example;
## 将一个模板类设置封装到C#中
%template(ExampleList)dan::List<Example>;
##修改由C++转为C#的方法(如希望改写C++某个方法的实现,或者为这个方法增加额外的实现),
##也可以是修改C#需要继承的某个方法。 也可以定义某个变量的输出类型代码
//dan::Smart_Object<ISGDrawControl> createObject 这个是函数名字(不需要带上函数参数名)
%typemap(csout, excode=SWIGEXCODE) dan::Smart_Object<ISGDrawControl> createObject {
global::System.IntPtr cPtr =$imcall; //这部分代码是由C++转C#的实现代码
if(cPtr==null)
{
return null;
}
var result_val= new ISGDrawControl(cPtr,true);
result_val._init();
return result_val;
}
// 保护方法继承
ps: %feature,%template类似的定义其实是SWIG中的宏,这种宏与我们的C++宏类似,在SWIG种定义了很多这种宏,当然我们也可以定义宏;
方法与参数封装
方法的封装更多的是参数的封装问题,SWIG有一套类型映射机制,简单来说就是一套规则,告诉swig如何将这些矛盾化解,并定义名称参数,将名称参数写道接口文件的类和函数声明。
##方法命名修改
%rename(plus) add;//在类中修改;
参数处理
- 输入参数转换(
in、csin
类型映射) - 重载方法中的输入参数类型检查(
typecheck
类型映射) - 输出参数处理(
argout
类型映射) - 输入参数值检查(
check
类型映射) - 输入参数初始化(
arginit
类型映射) - 默认参数(
default
类型映射) - 输入参数资源管理(
freearg
类型映射) - 输出参数转换(
out、csout
类型映射)
作为回调类的输入参数与正常的输入参数不同。类似的有
directorin、csdirectorin、csdirectorin、csdirectorout
swig基本类型说明
- swigtype 代表的是C++的当前类型,也可理解为通用类型
- ctype 代表是C语言中的类型(C++转C语言代码,只有基础类型和指针,所以类的处理大部分都是处理为指针类型)
- cstype 代表的是C#中的类型
## 综合示例 自定义字符串类型SGString与C#String类型的转换
//我们需要明白的是,SGString和String是两个不同的类,与SGString封装的代理类不同的是,C#的String不是一个代理类,它是C#中一个普通的类,SGString和String
//转换的中间条件是一定要能够转到基础类型中(SGString和String的基础类型是char*)
## 第一步,我们需要规定C++类型(SGString)对应的C#类型(String)
//需要注意的是SGString与const SGString&,SGString&,SGString是不同的类型,全面支持这几种类型需要多重复几次;
%typemap(ctype) SGString "char *" //C++类型到C类型(原类型SGString类型,char*类型是C类型)
%typemap(imtype) SGString "string" //C++到C#的中间类型,一般情况下等于C#类型,不同过多的考虑
%typemap(cstype) SGString "string"//C++类型到C#类型,明确这两种类型的关系;
## 第二步,确定C++类型到C类型的映射
//输入映射,从C++到C类型的映射,类型是SGtring类型,当然这个还可以有各种过滤
//如%typemap(in) SGString name 代表的是只对参数名为name的SGString类型生效
%typemap(in) SGString //注意的是SGString*,const SGString&与SGString不是同一个参数类型,如果要支持多个类型需要用逗号分开
%{ if (!$input) { //$input是输入参数;
return $null;
}
$1=$input.c_str();//$1是输入参数的中间参数,我们需要将输入参数转到中间参数,这个里面我们只有一个输入参数.
%}
%typemap(out) SGString %{ $result = $1.c_str(); %} //$result 为返回值标记;
## 第三步 确定回调类中SGString类型的处理方式;
//回调类中SGString作为参数的处理;
%typemap(directorout) SGString
%{ if (!$input) {
return $null;
}
$result=$input.c_str();
%}
%typemap(directorin) SGString //回调类从C#传入值到C++中;
%{
$input =$1.c_str();
%}
## 第四步 确定在C#代码中,对于SGString的处理方式(在开始的时候确定了SGString在C#表示为String);
%typemap(csin) SGString "$csinput"
%typemap(csout) SGString {
string ret = $imcall;$excode
return ret;
}