[SWIG] SWIG对Class包装的原理(以C#为例)

2024-02-13 11:48

本文主要是介绍[SWIG] SWIG对Class包装的原理(以C#为例),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在《SWIG原理(以C#为例)》之后,相信你对SWIG的原理有一定的了解了。
但似乎不太尽兴,SWIG对类是怎么包装的?
本文将以C#为例,讲解SWIG对类包装的原理。

文章目录

    • 代码框架
    • simple模块(C/C++代码)
    • usesimple(C#如何使用C/C++代码)
    • 中间层
      • simple_csharp/person.cs(提供给C#的接口文件)
      • simple_wrap模块(将C/C++代码进行包装)
      • simple_csharp/simplePINVOKE.cs(C#调用C++)
    • 总结

代码框架

本文代码:https://github.com/geodoer/swig-examples/tree/main/A-HowSWIGWorkClass

本示例的代码框架与《SWIG原理(以C#为例)》一样,只不过将example.h改成了person.h
请添加图片描述

simple模块(C/C++代码)

同样的,先来看看C/C++代码。
代码很简单,是一个简易的Person
而我们的目标就是在C#程序中,能够使用到这个Person类。

//File: person.h
class SIMPLE_EXPORT Person
{
public:Person(int guid);/*** @brief 系统唯一ID* @return string*/int Guid() const;/*** @brief Get the Name object* @return string*/char GetName() const;/*** @brief Set the Name object* @param  name*/void SetName(char name);/*** @brief Print Person's Information*/void Print() const;
protected:int		m_guid;char	m_name;
};

usesimple(C#如何使用C/C++代码)

了解了C/C++代码之后,我们再看看C#会如何使用我们的代码

//File: Program.cs//Person是托管对象,使用using
using (Person person = new Person(5))
{Console.WriteLine(person.Guid());Console.WriteLine(person.GetName());person.SetName('Q');Console.WriteLine(person.GetName());person.Print();
}

中间层

simple_csharp/person.cs(提供给C#的接口文件)

是的,由于Person是外部(C/C++)所提供的对象。所以在C#中,Person要实现IDisposable接口

  1. 在创建Person对象时,调用C/C++的代码,将对象new出来,然后保存该对象的C指针
  2. 在释放Person资源的时候,再次调用C/C++中的delete方法,将这个C指针回收
/* 提供给C#客户端调用的接口 */
/* Person是外部(C++)提供的对象,所以是托管对象,要实现IDisposable接口 */
public class Person : global::System.IDisposable
{/// C指针private global::System.Runtime.InteropServices.HandleRef swigCPtr;/// this是否属于C的内存protected bool swigCMemOwn;internal Person(global::System.IntPtr cPtr, bool cMemoryOwn){swigCMemOwn = cMemoryOwn;swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);}internal static global::System.Runtime.InteropServices.HandleRef getCPtr(Person obj){return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;}~Person(){Dispose(false);}public void Dispose(){Dispose(true);global::System.GC.SuppressFinalize(this);}protected virtual void Dispose(bool disposing){lock (this){if (swigCPtr.Handle != global::System.IntPtr.Zero){//释放这个C指针if (swigCMemOwn){swigCMemOwn = false;simplePINVOKE.delete_Person(swigCPtr);}swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);}}}public Person(int guid) : this(simplePINVOKE.new_Person(guid), true){}public int Guid(){int ret = simplePINVOKE.Person_Guid(swigCPtr);return ret;}public char GetName(){char ret = simplePINVOKE.Person_GetName(swigCPtr);return ret;}public void SetName(char name){simplePINVOKE.Person_SetName(swigCPtr, name);}public void Print(){simplePINVOKE.Person_Print(swigCPtr);}
}

simple_wrap模块(将C/C++代码进行包装)

根据Person.cs(提供给C#的接口文件),我们已经知道了C#要什么东西了

  1. new一个Person对象,并将其返回(注意,要将类型擦除)
  2. delete一个Person对象
  3. Person类的其他函数

因此,我们可以为C++的simple模块编写一个代理层,实现这些方法。

//File: person_wrap.cpp
EXPORT int STDCALL CSharp_Person_Guid(void * jarg1) {Person* arg1 = (Person *)jarg1;return ((Person const *)arg1)->Guid();
}EXPORT char STDCALL CSharp_Person_GetName(void * jarg1) {Person *arg1 = (Person *)jarg1;return (char)((Person const *)arg1)->GetName();
}EXPORT void STDCALL CSharp_Person_SetName(void * jarg1, char jarg2) {Person * arg1 = (Person *)jarg1;(arg1)->SetName(jarg2);
}EXPORT void STDCALL CSharp_Person_Print(void * jarg1) {Person *arg1 = (Person *)jarg1;((Person const *)arg1)->Print();
}EXPORT void * STDCALL CSharp_new_Person(int jarg1) {Person * result = (Person *)new Person(jarg1);return (void *)result;
}EXPORT void STDCALL CSharp_delete_Person(void * jarg1) {Person *arg1 = (Person *)jarg1;delete arg1;
}

simple_csharp/simplePINVOKE.cs(C#调用C++)

simple_csharp模块中的simplePINIVOKE.cs将通过动态链接库,调用在person_wrap模块中已经包装好C++函数,从而将整个流程打通,实现C#调用C++的通路。

/* C#中间层
1. 调用C++中间层的代码,完成C#与C++之间的通讯*/
class simplePINVOKE
{static simplePINVOKE(){}/// 从DLL中导入一个外部函数/// /// DllImport的参数说明/// 1. 第一个参数   simple_wrap          DLL的名称/// 2. 第二个参数   CSharp_new_Person    入口点,即函数名称[global::System.Runtime.InteropServices.DllImport("simple_wrap", EntryPoint = "CSharp_new_Person")]public static extern global::System.IntPtr new_Person(int jarg1);[global::System.Runtime.InteropServices.DllImport("simple_wrap", EntryPoint = "CSharp_Person_Guid")]public static extern int Person_Guid(global::System.Runtime.InteropServices.HandleRef jarg1);[global::System.Runtime.InteropServices.DllImport("simple_wrap", EntryPoint = "CSharp_Person_GetName")]public static extern char Person_GetName(global::System.Runtime.InteropServices.HandleRef jarg1);[global::System.Runtime.InteropServices.DllImport("simple_wrap", EntryPoint = "CSharp_Person_SetName")]public static extern void Person_SetName(global::System.Runtime.InteropServices.HandleRef jarg1, char jarg2);[global::System.Runtime.InteropServices.DllImport("simple_wrap", EntryPoint = "CSharp_Person_Print")]public static extern void Person_Print(global::System.Runtime.InteropServices.HandleRef jarg1);[global::System.Runtime.InteropServices.DllImport("simple_wrap", EntryPoint = "CSharp_delete_Person")]public static extern void delete_Person(global::System.Runtime.InteropServices.HandleRef jarg1);
}

总结

对于类,SWIG的原理也很简单,它为我们创建的中间层中,已经做好了资源的创建与回收工作。
因此,再提供给C#的接口中,Person是一个托管类。

至此,HelloSWIG已经完成。可以看出SWIG本质上就是一个代码生成器,为我们自动生成目标语言调用C++语言的中间层
但在实际工作中,这远远不够的,你还需要知道其他类型如何包装;如何配置SWIG的.i文件;如果处理模块与模块的依赖关系;如何把SWIG集成进工程中,自动化构建出中间模块等等。但其实这些在SWIG眼里都蛮简单的。

这篇关于[SWIG] SWIG对Class包装的原理(以C#为例)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/705453

相关文章

C#特性(Attributes)和反射(Reflection)详解

《C#特性(Attributes)和反射(Reflection)详解》:本文主要介绍C#特性(Attributes)和反射(Reflection),具有很好的参考价值,希望对大家有所帮助,如有错误... 目录特性特性的定义概念目的反射定义概念目的反射的主要功能包括使用反射的基本步骤特性和反射的关系总结特性

C#实现查找并删除PDF中的空白页面

《C#实现查找并删除PDF中的空白页面》PDF文件中的空白页并不少见,因为它们有可能是作者有意留下的,也有可能是在处理文档时不小心添加的,下面我们来看看如何使用Spire.PDFfor.NET通过C#... 目录安装 Spire.PDF for .NETC# 查找并删除 PDF 文档中的空白页C# 添加与删

通过C#获取Excel单元格的数据类型的方法详解

《通过C#获取Excel单元格的数据类型的方法详解》在处理Excel文件时,了解单元格的数据类型有助于我们正确地解析和处理数据,本文将详细介绍如何使用FreeSpire.XLS来获取Excel单元格的... 目录引言环境配置6种常见数据类型C# 读取单元格数据类型引言在处理 Excel 文件时,了解单元格

C#实现高性能Excel百万数据导出优化实战指南

《C#实现高性能Excel百万数据导出优化实战指南》在日常工作中,Excel数据导出是一个常见的需求,然而,当数据量较大时,性能和内存问题往往会成为限制导出效率的瓶颈,下面我们看看C#如何结合EPPl... 目录一、技术方案核心对比二、各方案选型建议三、性能对比数据四、核心代码实现1. MiniExcel

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

Spring框架中@Lazy延迟加载原理和使用详解

《Spring框架中@Lazy延迟加载原理和使用详解》:本文主要介绍Spring框架中@Lazy延迟加载原理和使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、@Lazy延迟加载原理1.延迟加载原理1.1 @Lazy三种配置方法1.2 @Component

spring IOC的理解之原理和实现过程

《springIOC的理解之原理和实现过程》:本文主要介绍springIOC的理解之原理和实现过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、IoC 核心概念二、核心原理1. 容器架构2. 核心组件3. 工作流程三、关键实现机制1. Bean生命周期2.

Redis实现分布式锁全解析之从原理到实践过程

《Redis实现分布式锁全解析之从原理到实践过程》:本文主要介绍Redis实现分布式锁全解析之从原理到实践过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、背景介绍二、解决方案(一)使用 SETNX 命令(二)设置锁的过期时间(三)解决锁的误删问题(四)Re

在.NET平台使用C#为PDF添加各种类型的表单域的方法

《在.NET平台使用C#为PDF添加各种类型的表单域的方法》在日常办公系统开发中,涉及PDF处理相关的开发时,生成可填写的PDF表单是一种常见需求,与静态PDF不同,带有**表单域的文档支持用户直接在... 目录引言使用 PdfTextBoxField 添加文本输入域使用 PdfComboBoxField

redis中使用lua脚本的原理与基本使用详解

《redis中使用lua脚本的原理与基本使用详解》在Redis中使用Lua脚本可以实现原子性操作、减少网络开销以及提高执行效率,下面小编就来和大家详细介绍一下在redis中使用lua脚本的原理... 目录Redis 执行 Lua 脚本的原理基本使用方法使用EVAL命令执行 Lua 脚本使用EVALSHA命令