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

本文详细介绍了SWIG如何将C++的类包装成C#可以使用的接口,包括C++代码、C#如何使用、中间层接口文件、包装模块以及C#调用C++的实现细节。通过示例展示了Person类的创建、使用和销毁过程,揭示了SWIG在跨语言调用中的作用。
摘要由CSDN通过智能技术生成

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

代码框架

本文代码: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眼里都蛮简单的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

geodoer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值