SWIG包装器使用指南——(四)C#使用SWIG简介与实践

Written by

in

文章目录
  • 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#数组元素的首指针封送了过去。
  • 部分C++类型映射到C#类型的规则如下: 更多默认规则,下载完SWIG之后,使用Everything软件搜索csharp.swg文件即可。
  • 是否还记得前几篇文章介绍typemap时的哪些in、out、freeargs方法? 上图里的方法也属于同等范畴,只不过是特定于C#语言映射时使用,后续你将会频繁使用。
  • 指令:%pragma(csharp) method={ … my code …}。 常用method有: imclassbase imclasscode imclassclassmodifiers imclassimports imclassinterfaces modulebase modulecode moduleimports … 等等 分别可以在不同的代理类的不同位置插入自己定义的代码。 如: %pragma(csharp) imclasscode={ … my code …}` 就是在moduleInvoke.cs文件里插入代码。
  • 理解一切皆是指针 避免在interface.i里写业务逻辑 注意SWIG指令顺序(大多%xxx在前,%include在后) 关注内存回收的节点,避免被过早GC 查看全文 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dgrt.cn/a/2147119.html 如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!
  • SWIG包装器使用指南——(四)C#使用SWIG简介与实践
  • C++ 判断字符串是否为UTF-8格式
  • CMake中引入指定路径下的OpenCV
  • 使用Python获取指定进程的CPU和内存使用情况
  • MacOS下把.mlmodel生成.mlmodelc
  • NSString和std::string互相转换
  • Socket bind() error: invalid operands to binary expression
  • Mac获取系统版本、机型
  • 源码编译clang
  • Mac编译踩坑记录
  • 设置或取得c# NumericUpDown 编辑框值的方法,(注意:不是Value值)
  • 使用NPOI 技术 的SetColumnWidth 精确控制列宽不能成功的解决办法(C#)
  • Mysql 数据库zip版安装时basedir datadir 路径设置问题,避免转义符的影响
  • java stream sorted排序 考虑null值
  • spring @EnableConfigurationProperties 实现原理
  • postman请求https网址没有响应,但是用浏览器有响应,解决办法
  • java @Inherited注解的作用
  • spring mvc的两种部署到Servlet容器的方式:web.xml 、WebApplicationInitializer 以及WebApplicationInitializer原理分析
  • Spring @AliasFor 的三种用法,亲测可用,包含测试代码
  • java synchronized wait notify的使用场景及解决的问题
  • SWIG系列:http://t.csdn.cn/cIAcr

    • 一、简介
    • 二、全局函数、变量、常量
    • 三、继承
    • 四、传递指针、引用、数组与值
    • 五、基本类型的指针与引用
    • 六、基本类型的数组
    • 七、基本类型的默认map规则
    • 八、常用的typemap方法
    • 九、代码插入
    • 十、实践
      • 10.1 如何映射Foo*&到ref Foo
      • 10.2 映射Foo* array数组
      • 10.3 如何将char**映射为string[]
      • 10.4 如何映射C++的容器(vector\pair)
      • 10.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#数组元素的首指针封送了过去。

    部分C++类型映射到C#类型的规则如下:
    在这里插入图片描述
    更多默认规则,下载完SWIG之后,使用Everything软件搜索csharp.swg文件即可。

    在这里插入图片描述
    是否还记得前几篇文章介绍typemap时的哪些in、out、freeargs方法? 上图里的方法也属于同等范畴,只不过是特定于C#语言映射时使用,后续你将会频繁使用。

    指令:%pragma(csharp) method={ … my code …}

    常用method有:

    • imclassbase
    • imclasscode
    • imclassclassmodifiers
    • imclassimports
    • imclassinterfaces
    • modulebase
    • modulecode
    • moduleimports
    • … 等等
      分别可以在不同的代理类的不同位置插入自己定义的代码。

    如:

    %pragma(csharp) imclasscode={ … my code …}`
    

    就是在moduleInvoke.cs文件里插入代码。

    class Spam 
    {public:void changeFoo(Foo*& f);
    };
    

    对于上述的C++代码,我们期望的调用方式为:

    var s = new Spam();
    var f = new Foo();
    s.changeFoo(ref f);
    

    需要进行如下typemap定义,一步一步的映射过去:

    // 告诉SWIG,C++的Foo*&就是C#的ref Foo
    %typemap(cstype) Foo*& "ref Foo";// 因为一切皆是指针,所以我们需要获取ref Foo的指针,供后续P/Invoke时传递。pre和post就是用来处理Foo与IntPtr的生成。
    %typemap(csin,
    pre="System.IntPtr temp_$csinput=Foo.getCPtr($csinput).Handle; System.IntPtr back_$csinput=temp_$csinput;",
    post="if(temp_$csinput!=back_$csinput) $csinput=new Foo(temp_$csinput,false);") Foo*& "ref temp_$csinput";// 因为一切皆是指针,所以我们就把指针传过去,并加上ref关键字,因为值会被修改。
    %typemap(imtype) Foo*& "ref System.IntPtr";// &是指针,*是指针,*&就是指针的指针,所以为void**
    %typemap(ctype) Foo*& "void**";
    

    其中cstypecsin具体指代如下图:
    在这里插入图片描述
    imtype指代如下:
    在这里插入图片描述
    ctype指代如下:
    在这里插入图片描述
    此时就可以正常调用了。

    简单类型的数组上面已经介绍过了,那么如何映射复杂类型的数组呢?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 List<object> _ref = new List<object>();	public void SetItemAndHold(int index,Foo value){_ref.Add(value); //将其添加一个列表里,使其不被GCsetitem(index,value);}
    %}
    

    然后将原有对setitem的调用重新调用到SetItemAndHold上即可。

    FooArray除了cast()之外另一个有用的方法就是frompointer(),它可以根据首元素读取一个数组:

    //f是C++层返回的一个值,是数组的首个元素
    FooArray array=FooArray.formpointer(f);
    // 后续对array操作,遍历
    

    使用如下的规则即可:

    CSHARP_ARRAYS(char *, string)%typemap(imtype, inattributes="[System.Runtime.InteropServices.In, System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPArray, SizeParamIndex=0, ArraySubType=System.Runtime.InteropServices.UnmanagedType.LPStr)]") char *INPUT[] "string[]";%apply char *INPUT[]  { char ** };
    

    class Spam 
    {
    public:void setFoo(std::vector<Foo> foos){}
    };
    

    与数组的映射基本一样:

    %include <std_vector.i>
    %template(FooVector) std::vector<Foo>;
    

    使用时:

     var s = new Spam();
    FooVector vec = new FooVector();
    vec.Add(new Foo());
    s.setFoo(vec);
    

    同数组类一样,setFoo也有内存过早被GC的问题,解决方式也是一样,不在赘述。

    记住一点:函数指针与普通对象指针没有什么不同。

    typedef void(*act)();
    class Spam 
    {
    public:void setAction(act a){}
    };
    

    对于act的包装,我们可以编写以下规则:

    %typemap(csin) void(*)() "System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate($csinput)";%typemap(imtype,out="IntPtr") 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

    查看全文

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dgrt.cn/a/2147119.html

    如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

    在这里插入图片描述

    SWIG包装器使用指南——(四)C#使用SWIG简介与实践

    SWIG系列:http://t.csdn.cn/cIAcr 文章目录一、简介二、全局函数、变量、常量三、继承四、传递指针、引用、数组与值五、基本类型的指针与引用六、基本类型的数组七、基本类型的默认map规则八、常用的typemap方法九、代码插入十、实践10.1 如何映射Foo*&到ref F……

    C++ 判断字符串是否为UTF-8格式

    原文地址 : http://www.zedwood.com/article/cpp-is-valid-utf8-string-function
    #include <iostream>using namespace std;bool utf8_check_is_valid(const string& string);int main(int argc, char *argv[])
    {string hello "hello world";……

    CMake中引入指定路径下的OpenCV

    在做图像处理时,通常情况下,直接在CMake中使用find_package命令,即可找到系统默认安装的OpenCV:
    find_package(OpenCV REQUIRED)执行cmake命令后,会找到OpenCV库,并输出路径和版本信息,比如在M……

    使用Python获取指定进程的CPU和内存使用情况

    参考链接:MacOS使用top命令查看进程使用内存
    import sys
    import time
    import os
    import psutil# 设置app名称,名称中不允许有空格,否则后面取top结果会错位
    if len(sys.argv) < 2:app_name "MyDemo"
    else:app_name str(sys.a……

    MacOS下把.mlmodel生成.mlmodelc

    /Applications/Xcode.app/Contents/Developer/usr/bin/coremlc compile $model_name . –deployment-target 14.0 –platform ios…

    NSString和std::string互相转换

    NSString -> std::string
    NSString *str "aaaaaa";
    std::string *string new std::string([str UTF8String]);
    // 记得释放stringstd::string -> NSString
    [NSString stringWithCString:str.c_str() encoding:[NSString defaultCStringEncoding]];…

    Socket bind() error: invalid operands to binary expression

    本文引用自StackOverflow。
    原始代码Code:
    if (bind(sockfd, (sockaddr *) &addr, sizeof(addr)) -1) {报错Error:
    fs_server.cpp:264:56: error: invalid operands to binary expression (__bind<int &, sockaddr *,unsigned long> and int)if (bind(sockfd……

    Mac获取系统版本、机型

    // 获取系统版本NSString *versionString;
    NSDictionary * sv [NSDictionary dictionaryWithContentsOfFile:"/System/Library/CoreServices/SystemVersion.plist"];
    versionString [sv objectForKey:"ProductVersion"];
    NSLog("%", versionSt……

    源码编译clang

    原链接:https://clang.llvm.org/get_started.html
    核心步骤如下:
    git clone https://github.com/llvm/llvm-project.git
    cd llvm-project
    git checkout 176249bd6732a8044d457092ed932768724a6f06 #check到自己需要的版本
    mkdir build
    cd build
    cmake……

    Mac编译踩坑记录

    M1机器为arm架构,无法启用sse优化。以下代码中的-msse4需要删除。set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -fpermissive -pthread -frtti -msse4 -fPIC")在高版本的系统环境下打包,为了兼容低版本的系统,需要显式声……

    设置或取得c# NumericUpDown 编辑框值的方法,(注意:不是Value值)

    本人在C#开发中使用到了NumericUpDown控件,但是发现该控件不能直接控制显示值,经研究得到下面的解决办法
    NumericUpDown由于是由多个控件组合而来的控件,其中包含一个类似TextBox的控件,若想取得或改变其中的值要使用如下方法
    N……

    使用NPOI 技术 的SetColumnWidth 精确控制列宽不能成功的解决办法(C#)

    在使用NPOI技术开发自动操作EXCEL软件时遇到不能精确设置列宽的问题。

    ISheet sheet1 hssfworkbook.CreateSheet("Sheet1");
    sheet1.SetColumnWidth(0, 50 * 256); // 在EXCEL文档中实际列宽为49.29
    sheet1.SetColumnWidth(1, 100 * 256); // 在EXCEL文……

    Mysql 数据库zip版安装时basedir datadir 路径设置问题,避免转义符的影响

    本人在开发Mysql数据库自动安装程序时遇到个很奇怪的问题,其中my.ini的basedir 的路径设置是下面这样的:
    basedir d:\测试\test\mysql
    但是在使用mysqld安装mysql服务时老是启动不了,报1067错误,后来查看window事件发现一个独特……

    java stream sorted排序 考虑null值

    项目里使用到排序, java里没有像C# 里的linq,只有stream,查找stream.sorted源码看到有个
    Comparator.nullsLast
    然后看了一下实现,果然是能够处理null值的排序,如:minPriceList.stream().sorted(Comparator.comparing(l -> l.g……

    spring @EnableConfigurationProperties 实现原理

    查看DataSourceAutoConfiguration源码,发现如下代码: Configuration ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) EnableConfigurationProperties(DataSourceProperties.class) Import({ DataSourcePoolMetadataProvidersCon……

    postman请求https网址没有响应,但是用浏览器有响应,解决办法

    遇到个问题:同一个get请求的url,postman请求https网址没有响应,但是用浏览器有响应
    url是https开头的,查看错误描述里有一个SSL的选项: 然后根据描述关掉这个选项: 然后就没问题了,能正常请求及……

    java @Inherited注解的作用

    看到很多注解都被Inherited进行了修饰,但是这个Inherited有什么作用呢?
    查看Inherited代码描述:
    Indicates that an annotation type is automatically inherited. If an Inherited meta-annotation is present on an annotation type decl……

    spring mvc的两种部署到Servlet容器的方式:web.xml 、WebApplicationInitializer 以及WebApplicationInitializer原理分析

    方式一、编写web.xml
    通常我们将一个spring mvc程序部署到Servlet容器(例如Tomcat)时,会使用该方式,示例如下:
    <web-app><listener><listener-class>org.springframework.web.context.ContextLoad……

    Spring @AliasFor 的三种用法,亲测可用,包含测试代码

    查看Spring中的AliasFor的文档,英文描述如下:
    Usage ScenariosExplicit aliases within an annotation: within a single annotation, AliasFor can be declared on a pair of attributes to signal that they are interchangeable aliases for each ot……

    java synchronized wait notify的使用场景及解决的问题

    首先请考虑这样的场景: 你用饿了么点了份外卖,然后你是希望外卖到了的时候外卖小哥主动打电话给你,还是你不停的打电话问外卖小哥有没有送到呢?
    分析一下这两种情况:
    一:外卖小哥主动打电话通知你外卖到了……

    评论

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注

    站内搜索