术→技巧, 研发

SWIG实现Python调用C/C++代码

钱魏Way · · 652 次浏览

SWIG简介

SWIG是Simplified Wrapper and Interface Generator的简称,它是一个能将C和C++的程序与其他各种高级语言诸如Perl,Python,Ruby和Tcl进行连结的开发工具。

目前支持的语言:

  • C# – Mono
  • C# – MS.NET
  • D
  • Go language
  • Guile
  • Java
  • Javascript – Node.js
  • Javascript – V8
  • Javascript – WebKit
  • Lua
  • MzScheme/Racket
  • OCaml
  • Octave
  • Perl
  • PHP
  • Python
  • R
  • Ruby
  • Scilab
  • Tcl/Tk

SWIG与Python

利用SWIG,Python可以现实以下功能:

  • 用Python调用C/C++库
  • 用Python继承C++类,并在Python中使用该继承类

理解脚本语言如何和C/C++交互,首先简单说一下Python的标准实现CPython,Python标准的解析器实现是由C编写的,基础功能模块也都是C编写的,然后将其编译成了Python解析器和相关so, 所以对于CPython来说,其本身解析过程最终都是通过执行底层C代码来进行实现的。官方标准CPython提供了对应的API允许对Python进行扩展,CPython扩展需要在C/C++代码中嵌入很多<Python.h>中的API,为了能够调用C/C++的函数,需要声明如何调用函数,参数的类型转换等等,很麻烦。

SWIG的目的就是要为C/C++ API提供脚本语言的接口,SWIG所有做的就是解决脚本语言和C/C++交互的问题,SWIG所做的事情其实就是两件事:

  • 根据要调用的C API生成Wrapper函数,作为胶水来让脚本解析器和底层C函数进行交互。
  • 为生成的Wrapper函数生成脚本语言的调用接口。

完成了对C/C++函数脚本语言接口的生成,通过直接使用脚本语言的接口,调用对应的Wrapper函数,Wrapper函数将脚本语言传入的参数,转换成C的参数,然后调用对应的C的接口,执行完后,Wrapper函数会将C返回的结果,转换成脚本语言的数据类型返回给脚本上层。

SWIG的安装

Windows

安装SWIG非常的简单,仅需要到官方下载后配置环境变量即可。

Linux

wget http://prdownloads.sourceforge.net/swig/swig-4.0.1.tar.gz
tar -zxvf swig-4.0.1.tar.gz
cd swig-4.0.1
./configure
make
sudo make install

安装完后需要添加路径到.bashrc以便于在任何目录下都可以操作swig的命令。

nano ~/.bashrc

# 添加以下两行到bashrc中
SWIG_PATH=/usr/local/share/swig/4.0.1
PATH=$PATH:$SWIG_PATH

source ~/.bashrc

使用swig -version 确认版本型号即为安装完成。

swig: error while loading shared libraries: libpcre.so.1: cannot open shared object file: No such file or directory

问题主要是swig使用时,找不到libpcre.so.1文件,我们在系统中查找以下它的位置:

sudo find / -name libpcre.so.1

查询后发现这个文件都在anaconda的子环境文件夹中:

/home/qw/anaconda3/lib/libpcre.so.1
/home/qw/anaconda3/pkgs/pcre-8.43-he6710b0_0/lib/libpcre.so.1

解决方案:创建软链接

sudo ln -s /home/qw/anaconda3/lib/libpcre.so.1 /usr/lib/libpcre.so.1

C语言示例

编写代码文件

1、编写C语言头文件example.h

int fact(int n);

2、 编写C语言源码example.c

#include "example.h"

int fact(int n) {
    if (n < 0) {
        return 0;
    }
    if (n == 0) {
        return 1;
    }
    else {
        return n * fact(n-1);
    }
}

3、 编写接口文件example.i

%module example

%{
#define SWIG_FILE_WITH_INIT
#include "example.h"
%}

int fact(int n)
  • %module后面的名字是被封装的模块名称,Python通过这个名称加载程序。
  • %{…%}之间所添加的内容,一般包含此文件需要的一些函数声明和头文件。
  • 最后一部分,声明了要封装的函数和变量。

使用命令行调用 Swig 方法产生 Python 模块

swig -python example.i

执行后会生成2个新的文件:example_wrap.c,example.py

利用 distutils 生成动态库

新建 setup.py,内容如下:

from distutils.core import setup, Extension

example_module = Extension('_example',
                           sources=['example_wrap.c', 'example.cpp'],
                           )

setup(name='example',
      version='0.1',
      author="SWIG Docs",
      description="""Simple swig example from docs""",
      ext_modules=[example_module],
      py_modules=["example"],
      )

编译生成库文件:

python setup.py build_ext –inplace

如果是Linux,执行完成后会在目录下生成类似 _example.cpython-37m-x86_64-linux-gnu.so 的文件

测试.so 文件能否顺利被python调用。在example目录下创建一个test.py文件。文件内容为

import example

print(example.fact(4))

其后执行python ./test.py看能正常的输出。

如果是Windows,则会在目录下生成类似 _example.cp37-win_amd64.pyd文件。调用方法稍有区别:

import _example

print(_example.fact(4))

参考链接:

发表评论

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