吕梁营销型网站建设费用,域名注册好如何网站建设,男人和女人做污的视频网站,东莞官方网站 优帮云SWIG系列#xff1a;http://t.csdn.cn/cIAcr 文章目录一、简介二、全局函数、变量、常量三、继承四、传递指针、引用、数组与值五、基本类型的指针与引用六、基本类型的数组七、基本类型的默认map规则八、常用的typemap方法九、代码插入十、实践10.1 如何映射Foo*到ref F…SWIG系列http://t.csdn.cn/cIAcr
文章目录一、简介二、全局函数、变量、常量三、继承四、传递指针、引用、数组与值五、基本类型的指针与引用六、基本类型的数组七、基本类型的默认map规则八、常用的typemap方法九、代码插入十、实践10.1 如何映射Foo*到ref Foo10.2 映射Foo* array数组10.3 如何将char**映射为string[]10.4 如何映射C的容器vector\pair10.5 如何映射函数指针十一、要点总结一、简介
SWIG如何实现让C#方便的调用C函数的
其实原理并不负责仍然使用C#的互操作技术P/Invoke实现只不过SWIG对C代码进行的包装使开发者更易于调用。
生成C#代码时注意可选添加几个额外的命令行选项
-dllimport 指定P/Invoke时要调用的dll名称-namespace 设置C#的命名空间-outfile 将所有生成的C#代码放到一个cs文件中
二、全局函数、变量、常量
int a 0;
void funA();
#define PI 3.14class Foo
{
public:
int * bar(int* items);
};
因C#里没有全局成员的概念所以SWIG都把它们生产到了与%module同名的类里且都是静态成员可直接访问。
三、继承
class Foo
{
};class Bar : public Foo
{};以上的c代码SWIG可以完整的生成为C#代码并且保留继承关系
public class Foo
{
};pulic class Bar : Foo
{};注意因C#里没有多继承所以如果C里的代码是多继承的则生成的C#代码只会继承第一个父类。 四、传递指针、引用、数组与值
class Foo
{
public:void spam1(Foo* x); // 传指针void spam2(Foo x); // 传引用void spam3(Foo x); // 传值void spam4(Foo x[]); // 传数组
};由于SWIG认为一切皆是指针所以上面的Foo* Foo Foo Foo[]四种类型并没有什么区别生成C#代理类后调用方式都一样
var f new Foo();
f.spam1(f);
f.spam2(f);
f.spam3(f);
f.spam4(f); //接收是数组但是我们只传了一个值进去。不符合期望后续要做处理。同理对于函数的返回值类型来说以下也没有什么区别
class Foo
{
public:Foo* spam1();Foo spam2();Foo spam3();
};五、基本类型的指针与引用
如果未做特殊说明这里的基本类型指的就是int、long、bool、char、float等这些类型非自己定义的类。
class Foo
{
public:void add(int* a, int* b, int* result) {*result *a *b;}
};默认情况下指针和引用会被生成为以SWIGTYPE_开头的C#类型
public void add(SWIGTYPE_p_int a, SWIGTYPE_p_int b, SWIGTYPE_p_int result)
{
}对于这些不太友好的类型需要编写.i文件进行映射将其映射为C#的int类型
%include typemaps.i%apply int *OUTPUT {int * result};
%apply int *INPUT {int *a,int *b};生成如下易于调用到C#代码
public void add(int a, int b, out int result)
{
}同理引用类型的处理也是类似可以将其映射为c#的ref
%include typemaps.i //添加SWIG自带的库%apply bool INOUT {bool};注在上述过程中我们并没有自己定义typemap。因为以上的操作SWIG已经帮我们实现了只是默认没有这么做而已其实现的代码就是写在了typemaps.i库我们只需要将其%apply一下。 六、基本类型的数组
C里的int*即可以表示单个对象也可以表示一个数组。这里演示如何将其映射为C#的数组。
class Foo
{
public:void copy(int* source, int* target, int count){}
};可以通过如下的定义将上述数组转为C#的数组
%include arrays_csharp.i // 添加SWIG自带的库
// apply一下
%apply int INPUT[] {int * source}
%apply int OUTPUT[] {int * target}然后就生成了如下的C#代码
public void copy(int[] source, int[] target, int count) {
demoModulePINVOKE.Foo_copy(swigCPtr, source, target, count);
}调用起来非常之方便我们观察下demoModulePINVOKE.Foo_copy方法的实现 原来是通过Marshal将C#数组元素的首指针封送了过去。
七、基本类型的默认map规则
部分类型映射到类型的规则如下 更多默认规则下载完SWIG之后使用Everything软件搜索csharp.swg文件即可。
八、常用的typemap方法 是否还记得前几篇文章介绍typemap时的哪些in、out、freeargs方法 上图里的方法也属于同等范畴只不过是特定于C#语言映射时使用后续你将会频繁使用。
九、代码插入
指令%pragma(csharp) method{ … my code …}。
常用method有
imclassbaseimclasscodeimclassclassmodifiersimclassimportsimclassinterfacesmodulebasemodulecodemoduleimports… 等等 分别可以在不同的代理类的不同位置插入自己定义的代码。
如
%pragma(csharp) imclasscode{ … my code …}就是在moduleInvoke.cs文件里插入代码。
十、实践
10.1 如何映射Foo*到ref Foo
class Spam
{public:void changeFoo(Foo* f);
};对于上述的C代码我们期望的调用方式为
var s new Spam();
var f new Foo();
s.changeFoo(ref f);需要进行如下typemap定义一步一步的映射过去
// 告诉SWIGC的Foo*就是C#的ref Foo
%typemap(cstype) Foo* ref Foo;// 因为一切皆是指针所以我们需要获取ref Foo的指针供后续P/Invoke时传递。pre和post就是用来处理Foo与IntPtr的生成。
%typemap(csin,
preSystem.IntPtr temp_$csinputFoo.getCPtr($csinput).Handle; System.IntPtr back_$csinputtemp_$csinput;,
postif(temp_$csinput!back_$csinput) $csinputnew Foo(temp_$csinput,false);) Foo* ref temp_$csinput;// 因为一切皆是指针所以我们就把指针传过去并加上ref关键字因为值会被修改。
%typemap(imtype) Foo* ref System.IntPtr;// 是指针*是指针*就是指针的指针所以为void**
%typemap(ctype) Foo* void**;其中cstype和csin具体指代如下图 imtype指代如下 ctype指代如下 此时就可以正常调用了。
10.2 映射Foo* array数组
简单类型的数组上面已经介绍过了那么如何映射复杂类型的数组呢Foo*可以直接映射为C#的Foo[]吗?
考虑有如下的C代码:
class Spam
{
public:void setFoo(Foo* array,int count){}
};在这里我们可以编写.i文件将Foo*映射为一个数组类但无法直接映射为C#的数组。
%include carrays.i // 添加库%array_class(Foo,FooArray); // 定义一个数组类其元素类型为Foo然后我们就可以通过FooArray这个数组类进行调用
var s new Spam();FooArray fooArray new FooArray(2);
for(int i 0; i 2; i)
{fooArray.setitem(i, new Foo());
}
//.cast()返回的类型正好就是Foo数组的第一个元素
s.setFoo(fooArray.cast(), 2);但是你仔细查看setitem的代码会发现 public void setitem(int index, Foo value) {demoModulePINVOKE.FooArray_setitem(swigCPtr, index, Foo.getCPtr(value)); }Foo.getCPtr(value)是获取到了value对象的指针传到C层setitem并未持有value对象自身的引用。这就会导致一个问题当你的应用程序面临很大的内存使用压力时value对象完全有可能被GC掉导致C调用出现异常。
如何解决过早被GC的问题
思路也很简单让setitem所在的对象继续持有value对象的引用即可当该对象被GC时才会顺便GCvalue而此时对C调用也一定已经结束了。
所以我们对FooArray类做如下的扩展添加了一个自定义方法SetItemAndHold
%typemap(cscode) FooArray %{ private Listobject _ref new Listobject(); public void SetItemAndHold(int index,Foo value){_ref.Add(value); //将其添加一个列表里使其不被GCsetitem(index,value);}
%}然后将原有对setitem的调用重新调用到SetItemAndHold上即可。
FooArray除了cast()之外另一个有用的方法就是frompointer()它可以根据首元素读取一个数组
//f是C层返回的一个值是数组的首个元素
FooArray arrayFooArray.formpointer(f);
// 后续对array操作遍历10.3 如何将char**映射为string[]
使用如下的规则即可
CSHARP_ARRAYS(char *, string)%typemap(imtype, inattributes[System.Runtime.InteropServices.In, System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPArray, SizeParamIndex0, ArraySubTypeSystem.Runtime.InteropServices.UnmanagedType.LPStr)]) char *INPUT[] string[];%apply char *INPUT[] { char ** };10.4 如何映射C的容器vector\pair
class Spam
{
public:void setFoo(std::vectorFoo foos){}
};与数组的映射基本一样
%include std_vector.i
%template(FooVector) std::vectorFoo;使用时 var s new Spam();
FooVector vec new FooVector();
vec.Add(new Foo());
s.setFoo(vec);同数组类一样setFoo也有内存过早被GC的问题解决方式也是一样不在赘述。
10.5 如何映射函数指针
记住一点函数指针与普通对象指针没有什么不同。
typedef void(*act)();
class Spam
{
public:void setAction(act a){}
};对于act的包装我们可以编写以下规则
%typemap(csin) void(*)() System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate($csinput);%typemap(imtype,outIntPtr) void(*)() System.IntPtr;// 将void(*)()映射为C#名为OperationFunction的委托
%typemap(cstype) void(*)() OperationFunction;//定义一个名为OperationFunction的委托
%pragma(csharp) moduleimports%{
public delegate void OperationFunction();
%}然后就可以使用原生的C#方式调用
var s new Spam();
OperationFunction action new OperationFunction(() { });
s.setAction(action);十一、要点总结
理解一切皆是指针避免在interface.i里写业务逻辑注意SWIG指令顺序大多%xxx在前%include在后关注内存回收的节点避免被过早GC