跨语言调用神器SWIG介绍与使用入门

安装#

依赖 PCRE 库

Copy
apt-get install libpcre2-dev

下载安装

Copy
$ ./configure $ make $ make install

介绍#

SWIG 是一个软件开发工具,能够简化不同编程语言与 C 和 C++ 程序连接的开发任务。
简而言之,SWIG 是一款编译器,它可以获取 C/C++ 声明并创建访问这些声明所需的包装器,从而可从包括 Perl、Python、Tcl、Ruby、Guile 和 Java 在内的其他语言访问这些声明。SWIG 通常不需要修改现有代码,而且通常只需几分钟即可构建一个可用的接口。

本质上,SWIG 是一个为生成代码而设计的工具,该工具可以让各种其他编程语言调用 C/C++ 代码。这些更高级的编程语言是 SWIG 代码生成器的目标语言,而 C 或 C++ 是输入语言。在运行 SWIG 时必须指定一种目标语言。这将为 C/C++ 和指定的目标语言生成代码,以便相互进行接口

这种将 C/C++ 与许多不同目标语言连接的能力是 SWIG 的核心优势和特性之一。

从一个c库开始#

假设,这里有一个c代码,我们要提供给python和golang、java等调用。

下面是代码:

Copy
/* File : example.c */ double My_variable = 3.0; /* Compute factorial of n */ int fact(int n) { if (n <= 1) return 1; else return n*fact(n-1); } /* Compute n mod m */ int my_mod(int n, int m) { return(n % m); }

你或许会想,是不是要先学习下python扩展如何写,JNI如何搞,cgo如何弄。。

但是,当你有了SWIG,这一切就变得更简单了。开始之前,我们首先需要编写一个swig接口.

swig接口#

swig接口,你可以理解为就像pb文件一样,要先定义一套标准的接口(interface),然后swig负责根据这个swig interface,去生成连接其他语言的胶水代码。

Copy
%module example %{ /* Put headers and other declarations here */ extern double My_variable; extern int    fact(int); extern int    my_mod(int n, int m); %} extern double My_variable; extern int    fact(int); extern int    my_mod(int n, int m);
  • 接口文件包含 C 函数原型和变量声明。
  • %module 指令定义了 SWIG 将创建的模块的名称。
  • {% %} 块提供了一个位置,用于将其他代码(如 C 头文件或附加 C 声明)插入到生成的 C 包装器代码中

接口就绪后,我们可以来生成对应语言的胶水代码了。

python调用c#

首先通过swig -{lang} example.i 生成wrap胶水代码。

Copy
swig -python example.i

这里会生成;

  • example_wrap.c 连接c代码的wrap
  • example.py 可以类比GRPC生成的访问wrap的代码。

我们来看下生成的包装代码example_wrap.c

Copy
/* ---------------------------------------------------------------------------- * This file was automatically generated by SWIG (https://www.swig.org). * Version 4.1.1 * * Do not make changes to this file unless you know what you are doing - modify * the SWIG interface file instead. * ----------------------------------------------------------------------------- */ /* source: example.i */ #define SWIG_VERSION 0x040101 #define SWIGGO #define SWIGMODULE example /* ----------------------------------------------------------------------------- * This section contains generic SWIG labels for method/variable * declarations/attributes, and other compiler dependent labels. * ----------------------------------------------------------------------------- */ /* template workaround for compilers that cannot correctly implement the C++ standard */ #ifndef SWIGTEMPLATEDISAMBIGUATOR # if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) # define SWIGTEMPLATEDISAMBIGUATOR template # elif defined(__HP_aCC) /* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ /* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ # define SWIGTEMPLATEDISAMBIGUATOR template # else # define SWIGTEMPLATEDISAMBIGUATOR # endif #endif /* inline attribute */ #ifndef SWIGINLINE # if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) # define SWIGINLINE inline # else # define SWIGINLINE # endif #endif /* attribute recognised by some compilers to avoid 'unused' warnings */ #ifndef SWIGUNUSED # if defined(__GNUC__) # if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) # define SWIGUNUSED __attribute__ ((__unused__)) # else # define SWIGUNUSED # endif # elif defined(__ICC) # define SWIGUNUSED __attribute__ ((__unused__)) # else # define SWIGUNUSED # endif #endif #ifndef SWIG_MSC_UNSUPPRESS_4505 # if defined(_MSC_VER) # pragma warning(disable : 4505) /* unreferenced local function has been removed */ # endif #endif #ifndef SWIGUNUSEDPARM # ifdef __cplusplus # define SWIGUNUSEDPARM(p) # else # define SWIGUNUSEDPARM(p) p SWIGUNUSED # endif #endif /* internal SWIG method */ #ifndef SWIGINTERN # define SWIGINTERN static SWIGUNUSED #endif /* internal inline SWIG method */ #ifndef SWIGINTERNINLINE # define SWIGINTERNINLINE SWIGINTERN SWIGINLINE #endif /* exporting methods */ #if defined(__GNUC__) # if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) # ifndef GCC_HASCLASSVISIBILITY # define GCC_HASCLASSVISIBILITY # endif # endif #endif #ifndef SWIGEXPORT # if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) # if defined(STATIC_LINKED) # define SWIGEXPORT # else # define SWIGEXPORT __declspec(dllexport) # endif # else # if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) # define SWIGEXPORT __attribute__ ((visibility("default"))) # else # define SWIGEXPORT # endif # endif #endif /* calling conventions for Windows */ #ifndef SWIGSTDCALL # if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) # define SWIGSTDCALL __stdcall # else # define SWIGSTDCALL # endif #endif /* Deal with Microsoft's attempt at deprecating C standard runtime functions */ #if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) # define _CRT_SECURE_NO_DEPRECATE #endif /* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ #if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) # define _SCL_SECURE_NO_DEPRECATE #endif /* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */ #if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES) # define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 #endif /* Intel's compiler complains if a variable which was never initialised is * cast to void, which is a common idiom which we use to indicate that we * are aware a variable isn't used. So we just silence that warning. * See: https://github.com/swig/swig/issues/192 for more discussion. */ #ifdef __INTEL_COMPILER # pragma warning disable 592 #endif #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> typedef ptrdiff_t intgo; typedef size_t uintgo; # if !defined(__clang__) && (defined(__i386__) || defined(__x86_64__)) # define SWIGSTRUCTPACKED __attribute__((__packed__, __gcc_struct__)) # else # define SWIGSTRUCTPACKED __attribute__((__packed__)) # endif typedef struct { char *p; intgo n; } _gostring_; typedef struct { void* array; intgo len; intgo cap; } _goslice_; static void Swig_free(void* p) { free(p); } static void* Swig_malloc(int c) { return malloc(c); } /* Put headers and other declarations here */ extern double My_variable; extern int fact(int); extern int my_mod(int n, int m); #ifdef __cplusplus extern "C" { #endif void _wrap_Swig_free_example_c8af3355f0aa50cc(void *_swig_go_0) { void *arg1 = (void *) 0 ; arg1 = *(void **)&_swig_go_0; Swig_free(arg1); } void *_wrap_Swig_malloc_example_c8af3355f0aa50cc(intgo _swig_go_0) { int arg1 ; void *result = 0 ; void *_swig_go_result; arg1 = (int)_swig_go_0; result = (void *)Swig_malloc(arg1); *(void **)&_swig_go_result = (void *)result; return _swig_go_result; } void _wrap_My_variable_set_example_c8af3355f0aa50cc(double _swig_go_0) { double arg1 ; arg1 = (double)_swig_go_0; My_variable = arg1; } double _wrap_My_variable_get_example_c8af3355f0aa50cc() { double result; double _swig_go_result; result = (double)My_variable; _swig_go_result = result; return _swig_go_result; } intgo _wrap_fact_example_c8af3355f0aa50cc(intgo _swig_go_0) { int arg1 ; int result; intgo _swig_go_result; arg1 = (int)_swig_go_0; result = (int)fact(arg1); _swig_go_result = result; return _swig_go_result; } intgo _wrap_my_mod_example_c8af3355f0aa50cc(intgo _swig_go_0, intgo _swig_go_1) { int arg1 ; int arg2 ; int result; intgo _swig_go_result; arg1 = (int)_swig_go_0; arg2 = (int)_swig_go_1; result = (int)my_mod(arg1,arg2); _swig_go_result = result; return _swig_go_result; } #ifdef __cplusplus } #endif

可以看到,自动生成的wrap代码为interface里的函数,变量都生成了包装函数,通过extern "C" 实现wrap函数导出,后续的其他编程语言可以通过访问这些导出的wrap函数,实现访问原来的c函数。

编译模块#

我们现在来生成一个python代码模块。

Copy
[root@dev swig_demo]#gcc -c -fpic example.c example_wrap.c -I/root/anaconda3/include/python3.9 [root@dev swig_demo]#gcc -shared example.o example_wrap.o -o _example.so [root@dev swig_demo]#ll total 244 drwxr-xr-x 3 root root 4096 Jan 18 19:40 ./ drwxr-xr-x 4 root root 4096 Jan 18 19:28 ../ -rw-r--r-- 1 root root 203 Jan 18 19:28 example.c -rw-r--r-- 1 root root 244 Jan 18 19:29 example.i -rw-r--r-- 1 root root 1560 Jan 18 19:39 example.o -rw-r--r-- 1 root root 2101 Jan 18 19:36 example.py -rwxr-xr-x 1 root root 52480 Jan 18 19:39 _example.so* -rw-r--r-- 1 root root 110727 Jan 18 19:36 example_wrap.c -rw-r--r-- 1 root root 53240 Jan 18 19:39 example_wrap.o [root@dev swig_demo]#python3 Python 3.9.13 (main, Aug 25 2022, 23:26:10) [GCC 11.2.0] :: Anaconda, Inc. on linux Type "help", "copyright", "credits" or "license" for more information. >>> import example >>> example.fact(4) 24 >>> example.my_mod(23, 7) 2 >>> example.cvar.My_variable + 4.5 7.5 >>>
  • swig -python example.i 会生成一个c包装代码example_wrap.c 和python 库代码example.py
  • example.py 里的函数定义和interface一致,实际上里面有代码去访问之前生成的wrap接口。
  • gcc -c -fpic example.c example_wrap.c -I/root/anaconda3/include/python3.9 这里编译代码,需要指定下python include代码,根据实际位置指定

与CMake等build system集成#

swig支持与cmake等集成,我们编写一个cmakelists.txt文件

Copy
FIND_PACKAGE(SWIG REQUIRED) INCLUDE(${SWIG_USE_FILE}) FIND_PACKAGE(PythonLibs) INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH}) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) SET(CMAKE_SWIG_FLAGS "") SET_SOURCE_FILES_PROPERTIES(example.i PROPERTIES CPLUSPLUS ON) SET_SOURCE_FILES_PROPERTIES(example.i PROPERTIES SWIG_FLAGS "-includeall") SWIG_ADD_MODULE(example python example.i example.c) SWIG_LINK_LIBRARIES(example ${PYTHON_LIBRARIES})

编译:

Copy
[root@dev swig_demo]#mkdir build [root@dev swig_demo]#cd build/ [root@dev build]#cmake .. [root@dev build]#make Scanning dependencies of target example_swig_compilation [ 25%] Swig compile example.i for python [ 25%] Built target example_swig_compilation [ 50%] Building CXX object CMakeFiles/_example.dir/CMakeFiles/_example.dir/examplePYTHON_wrap.o [ 75%] Building C object CMakeFiles/_example.dir/example.o [100%] Linking CXX shared module _example.so [100%] Built target _example [root@dev build]#ls CMakeCache.txt CMakeFiles cmake_install.cmake example.py _example.so Makefile [root@dev build]#ll total 104 drwxr-xr-x 3 root root 4096 Jan 18 19:51 ./ drwxr-xr-x 4 root root 4096 Jan 18 19:50 ../ -rw-r--r-- 1 root root 15949 Jan 18 19:50 CMakeCache.txt drwxr-xr-x 7 root root 4096 Jan 18 19:51 CMakeFiles/ -rw-r--r-- 1 root root 1662 Jan 18 19:51 cmake_install.cmake -rw-r--r-- 1 root root 2101 Jan 18 19:51 example.py -rwxr-xr-x 1 root root 57472 Jan 18 19:51 _example.so* -rw-r--r-- 1 root root 6688 Jan 18 19:51 Makefile

golang调用c#

Go 并不支持直接调用用 C/C++ 编写的函数。cgo 程序可用于生成调用 C 代码的包装器,但目前没有直接调用 C++ 代码的简单方式。SWIG 填补了这一空白。

默认情况下 SWIG 会生成可以直接被 go build 使用的文件。 Go 需要大于1.2 版本。

同样上面的example.i, 我们可以

Copy
swig -go example.i go install

上面的命令,会生成example.go

Copy
/* ---------------------------------------------------------------------------- * This file was automatically generated by SWIG (https://www.swig.org). * Version 4.1.1 * * Do not make changes to this file unless you know what you are doing - modify * the SWIG interface file instead. * ----------------------------------------------------------------------------- */ // source: example.i package example /* #define intgo swig_intgo typedef void *swig_voidp; #include <stddef.h> #include <stdint.h> typedef ptrdiff_t intgo; typedef size_t uintgo; typedef struct { char *p; intgo n; } _gostring_; typedef struct { void* array; intgo len; intgo cap; } _goslice_; extern void _wrap_Swig_free_example_c8af3355f0aa50cc(uintptr_t arg1); extern uintptr_t _wrap_Swig_malloc_example_c8af3355f0aa50cc(swig_intgo arg1); extern void _wrap_My_variable_set_example_c8af3355f0aa50cc(double arg1); extern double _wrap_My_variable_get_example_c8af3355f0aa50cc(void); extern swig_intgo _wrap_fact_example_c8af3355f0aa50cc(swig_intgo arg1); extern swig_intgo _wrap_my_mod_example_c8af3355f0aa50cc(swig_intgo arg1, swig_intgo arg2); #undef intgo */ import "C" import "unsafe" import _ "runtime/cgo" import "sync" type _ unsafe.Pointer var Swig_escape_always_false bool var Swig_escape_val interface{} type _swig_fnptr *byte type _swig_memberptr *byte func getSwigcptr(v interface { Swigcptr() uintptr }) uintptr { if v == nil { return 0 } return v.Swigcptr() } type _ sync.Mutex func Swig_free(arg1 uintptr) { _swig_i_0 := arg1 C._wrap_Swig_free_example_c8af3355f0aa50cc(C.uintptr_t(_swig_i_0)) } func Swig_malloc(arg1 int) (_swig_ret uintptr) { var swig_r uintptr _swig_i_0 := arg1 swig_r = (uintptr)(C._wrap_Swig_malloc_example_c8af3355f0aa50cc(C.swig_intgo(_swig_i_0))) return swig_r } func SetMy_variable(arg1 float64) { _swig_i_0 := arg1 C._wrap_My_variable_set_example_c8af3355f0aa50cc(C.double(_swig_i_0)) } func GetMy_variable() (_swig_ret float64) { var swig_r float64 swig_r = (float64)(C._wrap_My_variable_get_example_c8af3355f0aa50cc()) return swig_r } func Fact(arg1 int) (_swig_ret int) { var swig_r int _swig_i_0 := arg1 swig_r = (int)(C._wrap_fact_example_c8af3355f0aa50cc(C.swig_intgo(_swig_i_0))) return swig_r } func My_mod(arg1 int, arg2 int) (_swig_ret int) { var swig_r int _swig_i_0 := arg1 _swig_i_1 := arg2 swig_r = (int)(C._wrap_my_mod_example_c8af3355f0aa50cc(C.swig_intgo(_swig_i_0), C.swig_intgo(_swig_i_1))) return swig_r }

可以看到,生成的代码默认还是通过cgo去调用swig生成的包装器wrap代码。

本质上,SWIG和我们常用的GRPC起到类似的作用:-)

总结#

SWIG 建立起java、python等其他高级编程语言调用c/c++ 代码的桥梁,可以不用了解JNI、cgo等复杂的跨语言调用知识,实现一次编写接口,同步生成多语言接口。

关注作者

欢迎关注作者微信公众号, 一起交流软件开发:欢迎关注作者微信公众号

posted @   JadePeng  阅读(2445)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2021-01-18 2020年Javascript 前端框架google搜索排名
2020-01-18 容器环境的JVM内存设置最佳实践
点击右上角即可分享
微信分享提示
CONTENTS