资讯动态

swig 使用案例

发布时间:2026/10/1 16:44:40 来源:尧图企业网站定制
包含数组、结构体嵌套指针函数指针传递等基本操作。swig默认不支持数组元素的写入如果想操作数组元素可以附加一些接口函数实现。比如下面在处理结构体的数组成员时使用%extend命令扩展了对应的操作接口(也可以直接使用swig提供的array_functions功能此时要用到carrays.i接口定义文件)*注使用的swig版本为4.1.1一. 生成C#的接口/***** test.h******/ #ifndef _TEST_H_ #define _TEST_H_ #include stdint.h typedef double SEVEN_FIXED_ARRAY[7]; typedef enum _E_TYPE_ { E_TP_0 0, E_TP_1 1, } E_TYPE; typedef struct _OPTION_CONFIG_ { int csType; int csID; }OPTION_CONFIG; typedef struct _ST_PARAM_INFO_ { int nSp; int nBr; int nTid; OPTION_CONFIG cfg; int nWid; int szRes[4]; }ST_PARAM_INFO; typedef struct _ST_TARGET_ { int nId; E_TYPE etype; SEVEN_FIXED_ARRAY arr; }ST_TARGET_INFO; typedef struct _ST_MOV_PARAM_ { ST_TARGET_INFO TargetInfo; ST_PARAM_INFO ParamInfo; }ST_PARAM_NODE; typedef struct _ST_PARAM_LIST_ { int nNodeNum; ST_PARAM_NODE stParam[50]; }ST_PARAM_LIST; typedef struct _ST_MOTION_SEGMENT_STRU { __int32 iCount;//data中存放的ST_PARAM_NODE数据个数 __int32 iSegIdx;//轨迹段编号 ST_PARAM_NODE* data;//指向一片连续内存存放多个ST_PARAM_NODE数据 }ST_MOTION_SEGMENT; typedef int (*Fun)(void* msg,int msgLen, void* usrdata); void Test(ST_PARAM_NODE* param); void RegisterFunc(Fun usrfunc,void* usrdata); #endif/************ test.c ********************/ #include stdio.h #include test.h static Fun dowork NULL; static void* workdata NULL; void Test(ST_PARAM_NODE* param) { printf( stMotionParam.nToolId:%d.\r\n , param-ParamInfo.nTid); OPTION_CONFIG cfg {.csID1,.csType2}; if (dowork ! NULL) dowork(cfg,sizeof(OPTION_CONFIG), workdata); return; } void RegisterFunc(Fun usrfunc, void* usrdata) { dowork usrfunc; workdata usrdata; }/* command: swig.exe -csharp -namespace ModuleTest -outdir ./interfaces example.i*/ /***********************example.i*****************/ %module TEST %include windows.i %include carrays.i %include cdata.i %include stdint.i %include cpointer.i %{ #include test.h %} %array_class(char, CharArray); %array_class(double, DoubleArray); %array_functions(ST_PARAM_NODE,ParamNodeArrayOperations); %array_class(ST_PARAM_NODE,ParamNodeArray); %pointer_functions(long long int,Int64PtrOp) %apply void *VOID_INT_PTR {void *} %include test.h %extend _ST_PARAM_INFO_ { int set_szRes(int* res,int count) { if(count1||count4) return -1; else { memcpy(self-szRes,res,count*sizeof(int)); return count; } } int get_szRes(int* res,int count) { if(count1||count4) return -1; else { memcpy(res,self-szRes,count*sizeof(int)); return count; } } int getszResByIndex(int idx) { if(idx0||idx3) return 0x7FFFFFFF; else return self-szRes[idx]; } }; %extend _ST_PARAM_LIST_ { // Attach these functions to struct _ST_PARAM_LIST_ int set_stParam(int idx,ST_PARAM_NODE* mp) { if(idx0||idxself-nNodeNum-1) return -1; self-stParam[idx] *mp; return 0; } int get_stParam(int idx,ST_PARAM_NODE* mp) { if(idx0||idxself-nNodeNum-1) return -1; *mp self-stParam[idx]; return 0; } }; %extend _ST_TARGET_ { int set_arr(double* p, int count) { int posSz sizeof(self-arr)/sizeof(double); if(pNULL||count1||countposSz) return -1; memcpy(self-arr,p,count*sizeof(double)); return count; } int get_arr(double* p, int count) { int posSz sizeof(self-arr)/sizeof(double); if(pNULL||count1||countposSz) return -1; memcpy(p,self-arr,count*sizeof(double)); return count; } }; %inline %{ int SegmentTest(const void* seg, int sz) { int res 0; ST_MOTION_SEGMENT* segPtr (ST_MOTION_SEGMENT*)seg; if(sz ! sizeof(ST_MOTION_SEGMENT)) return -1; for(int i0;isegPtr-iCount;i) res (i1) * segPtr-data-TargetInfo.nId res; return res; } static int HandleInternal0; int GreateHandle(void* handle)//void 二级指针传出 { long long int addr (long long int)HandleInternal; *(long long int*)handle addr; printf(Handle address: %#x.\r\n,addr); return 0; } void ProcessHandle(void* handle)//void 二级指针传入 { int* handlePtr *(long long int*)(handle); if(handlePtr ! NULL) { printf(Process Handle address: %#x.\r\n,handlePtr); *handlePtr 1; } printf(HandleInternal Update: %d.\r\n,HandleInternal); } %}/******************************Program.cs*********************************/ using System; using System.Runtime.InteropServices; using ModuleTest; namespace CsharpClient { class Test { [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate int MyFuncType(IntPtr msgPtr, int msgLen, IntPtr userobj); public static MyFuncType delegateFunc new MyFuncType(MyFunc); public static int MyFunc(IntPtr msgPtr, int msgLen, IntPtr userObj) { if (msgPtr IntPtr.Zero || userObj IntPtr.Zero|| msgLen 1) { Console.WriteLine(Invalid callback data.); return -1; } GCHandle handle GCHandle.FromIntPtr(userObj); Test user (Test)handle.Target; OPTION_CONFIG cfg new OPTION_CONFIG(msgPtr, false); Console.WriteLine(MyFunc Called: {0},obj-x{1},obj-y{2}, cfg.csID, user.id, user.age); return 0; } public int id 23; public int age 44; }; class Program { static void Main(string[] args) { Test user new Test(); GCHandle handle GCHandle.Alloc(user,GCHandleType.Pinned); IntPtr handlePtr handle.AddrOfPinnedObject(); SWIGTYPE_p_f_p_void_int_p_void__int funcptr new SWIGTYPE_p_f_p_void_int_p_void__int(Marshal.GetFunctionPointerForDelegate(Test.delegateFunc), false); //SWIGTYPE_p_void userPtr new SWIGTYPE_p_void(handlePtr, false); TEST.RegisterFunc(funcptr, handlePtr); { ST_PARAM_LIST ff new ST_PARAM_LIST(); ff.nNodeNum 9; int[] szResIn new int[4]; int[] szResOut new int[4]; szResIn[0] 1; szResIn[1] 2; szResIn[2] 3; szResIn[3] 4; IntPtr pszIn Marshal.UnsafeAddrOfPinnedArrayElement(szResIn, 0); IntPtr pszOut Marshal.UnsafeAddrOfPinnedArrayElement(szResOut, 0); ST_PARAM_NODE paraIn new ST_PARAM_NODE(); ST_PARAM_NODE paraOut new ST_PARAM_NODE(); int i 0; for (; i ff.nNodeNum; i) { paraIn.TargetInfo.nId i; SWIGTYPE_p_int szIn new SWIGTYPE_p_int(pszIn, false); /*************** Set 方法设置*********************/ //paraIn.stMotionParam.szRes szIn; /*************** 自定义接口设置*******************/ paraIn.ParamInfo.set_szRes(szIn, 4); ff.set_stParam(i, paraIn); } i 0; for (; i ff.nNodeNum; i) { ff.get_stParam(i, paraOut); //SWIGTYPE_p_int szout paraIn.ParamInfo.szRes; /**************遍历访问*********************/ //int j 0; //for (; j 4; j) { // IntPtr vptr SWIGTYPE_p_int.getCPtr(szout).Handle j; // szResOut[j] System.Runtime.InteropServices.Marshal.ReadInt32(vptr); //} /**************批量复制**********************/ //Marshal.Copy(SWIGTYPE_p_int.getCPtr(szout).Handle, szResOut, 0,4); /***************接口接收*********************/ SWIGTYPE_p_int szout new SWIGTYPE_p_int(pszOut, false); paraOut.ParamInfo.get_szRes(szout,4); Console.WriteLine(target id{0},sz1{1}, paraOut.TargetInfo.nId, szResOut[1]); } ST_MOTION_SEGMENT seg new ST_MOTION_SEGMENT(); ParamNodeArray nodeArr new ParamNodeArray(5); nodeArr.setitem(0,paraIn); nodeArr.setitem(1,paraIn); nodeArr.setitem(2,paraIn); nodeArr.setitem(3,paraIn); nodeArr.setitem(4,paraIn); ST_PARAM_NODE nodeRef new ST_PARAM_NODE(ParamNodeArray.getCPtr(nodeArr).Handle,false); seg.data nodeRef; seg.iCount 5; //SWIGTYPE_p_void segPtr new SWIGTYPE_p_void(ST_MOTION_SEGMENT.getCPtr(seg).Handle, false); int res TEST.SegmentTest(ST_MOTION_SEGMENT.getCPtr(seg).Handle, 24); TEST.Test(paraOut); SWIGTYPE_p_long_long pint TEST.new_Int64PtrOp(); IntPtr pptr SWIGTYPE_p_long_long.getCPtr(pint).Handle;//二级指针 TEST.CreadteHandle(pptr); TEST.ProcessHandle(pptr); TEST.delete_Int64PtrOp(pint); } if(handle.IsAllocated) handle.Free(); } } }二.生成python的接口1. 修改接口文件example.i 实现C风格的回调函数注册。/* command: swig.exe -python -outdir ./ example.i*/ %module TEST %include windows.i %include stdint.i %include cdata.i %include pybuffer.i %{ #define SWIG_FILE_WITH_INIT #include test.h %} %include test.h %inline%{ struct CallbackData { PyObject *PyFunc; PyObject *PyData; //CallbackData(PyObject* func,PyObject* usrdata)//Construct //{ // PyFunc NULL; // PyData NULL; // if(func ! NULL PyCallable_Check(func)) // { // PyFunc func; // Py_INCREF(func); // } // if(usrdata ! NULL) // { // PyData usrdata; // Py_INCREF(usrdata); // } //} //~CallbackData()//Deconstruct //{ // if(PyFunc ! NULL) // Py_DECREF(PyFunc); // if(PyData ! NULL) // Py_DECREF(PyData); //} }; %} %extend CallbackData { void Construct(PyObject* func,PyObject* usrdata) { self-PyFunc NULL; self-PyData NULL; if(func ! NULL PyCallable_Check(func)) { self-PyFunc func; Py_INCREF(func); } if(usrdata ! NULL) { self-PyData usrdata; Py_INCREF(usrdata); } } void Deconstruct() { if(self-PyFunc ! NULL) { Py_DECREF(self-PyFunc); self-PyFunc NULL; } if(self-PyData ! NULL) { Py_DECREF(self-PyData); self-PyData NULL; } } }; %{ static int PythonCallBack(void* msg, int msgLen,void *usrdata) { int dres 0; PyObject *arglist; PyObject *result; void* udata_void_ptr; struct CallbackData* udata_ptr (struct CallbackData*)0; PyGILState_STATE state PyGILState_UNLOCKED; int gilOk 0; if(PyGILState_Check()!1) { state PyGILState_Ensure(); gilOk 1; } int res SWIG_ConvertPtr((PyObject *)usrdata, udata_void_ptr,SWIGTYPE_p_CallbackData, 0); if (!SWIG_IsOK(res)) { SWIG_exception_fail(SWIG_ArgError(res), userdata registered Wrong);//goto fail when Macro expand. } udata_ptr (struct CallbackData *)(udata_void_ptr); if (udata_ptr-PyFunc NULL) { SWIG_exception_fail(SWIG_ArgError(-1), regist callback data is not constructed.); } res PyCallable_Check(udata_ptr-PyFunc); if (!SWIG_IsOK(res)) { SWIG_exception_fail(SWIG_ArgError(res), callback registered does not have a callable object!); } PyObject* msgdata SWIG_NewPointerObj(SWIG_as_voidptr(msg), SWIGTYPE_p__OPTION_CONFIG_, 0); /* static PyObject* msgdata NULL;//could not be static if PythonCallBack would be called in a thread that is not the same thread that created the instance originally. if(msgdata NULL) { msgdata SWIG_NewPointerObj((OPTION_CONFIG*)memcpy((OPTION_CONFIG*)calloc(1,sizeof(OPTION_CONFIG)),msg,sizeof(OPTION_CONFIG),SWIGTYPE_p__OPTION_CONFIG_,SWIG_POINTER_OWN|0); } else { if(SwigPyPacked_Check(msgdata)) { SwigPyPacked* packed_obj (SwigPyPacked*)msgdata; SWIG_ConvertPacked(msgdata,msg,sizeof(OPTION_CONFIG), packed_obj-ty); } } */ if(msgdata ! NULL) { //arglist Py_BuildValue((OiO),msgdata,msgLen,udata_ptr-PyData); //result PyEval_CallObject(udata_ptr-PyFunc,arglist); //Py_DECREF(arglist); PyObject *msgLenObj PyLong_FromLong(msgLen); resultPyObject_CallFunctionObjArgs(udata_ptr-PyFunc,msgdata,msgLenObj, udata_ptr-PyData,NULL); if (result) { dres PyInt_AsLong(result); } Py_XDECREF(result); Py_XDECREF(msgLenObj); } fail: Py_XDECREF(msgdata);//Note: comment this line if msgdata is static if(gilOk) PyGILState_Release(state); return dres; } %} %inline %{ void RegisterFunc_wrapper(PyObject *cbData) { void* vain_ptr; int res SWIG_ConvertPtr(cbData, vain_ptr,SWIGTYPE_p_CallbackData,0); if(SWIG_IsOK(res)) { struct CallbackData * cbinfo (struct CallbackData *)(vain_ptr); if(cbinfo-PyFunc ! NULL PyCallable_Check(cbinfo-PyFunc)) { RegisterFunc(PythonCallBack, (void *) cbData); return; } } SWIG_Error(res,regist callback data failed.); } %} %pybuffer_mutable_binary(char *data, size_t size); %pybuffer_binary(const char* data, size_t size); %inline %{ int PyReadBinaryData(char *data, size_t size) { int ret snprintf(data, size, %s, hello); return ret; } int PySendBinaryData(const char* data, size_t size) { return size; } %}2. 修改example.i接口文件利用features 面向对象特性实现回调函数的注册。/* command: swig.exe -c -python -outdir ./ example.i*/ %module(directors1) TEST %include windows.i %include stdint.i %include carrays.i %include cdata.i %{ #define SWIG_FILE_WITH_INIT #ifdef __cplusplus extern C { #endif #include test.h #ifdef __cplusplus } #endif %} #ifdef __cplusplus extern C { #endif %include test.h #ifdef __cplusplus } #endif %array_functions(int,intArray); %array_functions(ST_PARAM_NODE,paramNodeArray); %feature(director) BinaryOp; %inline %{ struct BinaryOp { virtual int handle(OPTION_CONFIG* msg, int msgLen, void* data) 0; virtual ~BinaryOp() {} }; %} %{ static int CallbackInternal(void* msg, int msgLen, void* usrdata) { if(usrdata ! NULL) { BinaryOp* handler_ptr (BinaryOp*)usrdata return handler_ptr-handle((OPTION_CONFIG*)msg, msgLen, usrdata); } else return 0; } %} %inline %{ void RegisterFunc_wrapper(BinaryOp *handler, void* usrdata) { RegisterFunc(CallbackInternal,(void*)handler); handler NULL; return; } %}3. 编写python打包脚本。如果使用1中的接口定义swig生成的接口文件example_wrap.c为C语言风格如果使用2中的接口定义则生成的example_wrap.cxx为c风格。因此在写python打包脚本时Extension的sources中要根据实际情况选择example_wrap.c或者example_wrap.cxx。#!/usr/bin/env python setup.py file for SWIG example from distutils.core import setup, Extension swig生成的example_wrap.c 中有一个SWIG_init宏定义,根据python版本不同会有所区别 即PyInit_xxxx。 python3以上版本强制以下划线开头。因此这里的Extension扩展模块 的名字也要设为下划线模块名。 如果test.c还用到了第三方库则可以在library_dirs和libraries中进行配置 example_module Extension(_TEST, sources[example_wrap.cxx, test.c], include_dirs[.,], library_dirs[.,], libraries[], ) py_modules 指定要打包的模块指定为上面的Extension扩展模块。 setup (name my_test_package, version 0.1, author SWIG Docs, description Simple swig example from docs, ext_modules [example_module], py_modules [_TEST], )4. 执行python打包命令, 得到python可用的包(生成的_TEST.pyd文件)python setup.py build_ext --inplace5. 可以把_TEST.pyd文件和swig生成的TEST.py文件拷贝到python项目中进行测试。如果3中用的是example_wrap.cxx则需要继承实现虚函数重写回调函数实现。如果用的是example_wrap.c则不需要做额外的操作。import ctypes import struct import TEST class PythonBinaryOp(TEST.BinaryOp): # Define Python class constructor def __init__(self): # Call C base class constructor TEST.BinaryOp.__init__(self) # Override C method: virtual int handle(OPTION_CONFIG* msg, int msgLen, void* data) 0; def handle(self, msg, msgLen, usrdata): # print(help(msg)) print(msg: ,msg.thisown, msg.csID, msg.csType) print(msgLen: ,msgLen) print(usrdata: , usrdata) return 0 if __name__ __main__: # print(help(TEST)) ss hello world c_s ctypes.c_wchar_p(ss) c_void_p_ss ctypes.cast(c_s, ctypes.c_void_p) handler PythonBinaryOp() TEST.RegisterFunc_wrapper(handler, None) # can not pass c_void_p_ss here node TEST.ST_PARAM_NODE() TEST.intArray_setitem(node.ParamInfo.szRes, 0, 5) print(TEST.intArray_getitem(node.ParamInfo.szRes, 0)) binary_szRes TEST.cdata(node.ParamInfo.szRes, 4*4).encode() print(binary_szRes len: ,binary_szRes.__len__()) print(binary_szRes data: ,binary_szRes) print(struct.unpack(iiii,binary_szRes)) TEST.Test(node)import TEST def xxxx(msg, msgLen, pyobj): print(msg: ,type(msg),msg.thisown,msg.csID) print(msgLen: ,msgLen) print(usrdata: , pyobj) return 0 if __name__ __main__: #print(help(TEST)) buffer bytearray(10) TEST.PyReadBinaryData(buffer) TEST.PySendBinaryData(bhello world) ss hello world cbdata TEST.CallbackData() cbdata.Construct(xxxx,ss) TEST.RegisterFunc_wrapper(cbdata) #cbdata.Deconstruct() node TEST.ST_PARAM_NODE() TEST.Test(node)6. 关于python的回调由于python有一个全局解释锁GIL在执行python代码时必须先要拿到这个锁。这样一来python向c/c模块中注册的函数在c/c发起调用时也受到该锁的约束。如果c/c底层有其他非python线程异步执行python任务在没有显式地拿锁的情况下这些线程直接调用python的回调接口将引起GIL锁竞争异常造成python内部对象引用计数出现混乱出现gc object already tracked引发GC崩溃。因此在多线程环境中需要先使用Py_Initialize给线程提供执行环境并在调用python回调函数时提前拿锁PyGILState_Ensure()/PyGILState_Release()如上面第一小节中接口封装示例(注意必须在创建任何PyObject对象之前就执行拿锁操作否则这些对象的引用计数会有问题)。三. 有选择地导出接口或数据结构很多时候我们并不想把c所有的数据结构或者接口都导出来暴露过多细节这种情况下应该对c/c接口头文件进行拆分(阅读文档相关章节了解详细内容SWIG Basics)。四. 没有银弹起初接触到swig时发现它竟然支持那么多种语言的转换简直惊为天人。确实很强大但是在使用过程中发现还是存在一些问题。往往c/c底层模块的提供方只提供c/c风格的接口而使用方想要用的时候如果不熟悉c/c的话接口.i文件中也很难去实现数据类型转换生成的接口基本都是要求指针操作。为了在c#或python这样的语言中调用这些接口则必须做一系列操作将原生C#或python数据对象的指针取出来传给接口。虽然swig本意是要求使用方直接使用接口提供的数据类型而非原生数据但程序员应该更习惯用原生数据所以更倾向于做这些转换动作。其中函数指针是一个特殊的数据类型这种转换可能更加复杂。比如要在python代码中向swig接口注册一个c风格的回调函数需要编写CPython代码在底层实现数据转换这实在太费劲了。如果可以尽量利用C面向对象的多态性来实现直接继成swig生成的接口类并重写虚方法。对于C类型的库最好用C封装一层再用swig生成接口(类似上面在接口文件中定义了struct BinaryOp然后在python中继承并实现重载)。但是这样感觉已经背离了使用swig的初衷直接利用脚本语言自带的C/C扩展接口可能更方便。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑