使用 SWIG 为Python包装 C/C++ – Set 1
毫无疑问,C 比Python快,那么像Numpy这样的Python库如何如此快速高效地执行大量数字运算工作呢?实际上,像 Numpy 这样的库并不是完全用Python编写的,而是该库的某些部分是用 C 编写的,这提供了性能提升。在用 C 编写代码后,我们将它们包装在Python代码中,这就像这些 C 代码的接口一样。然后,我们可以使用Python语法调用 C 函数,其中实际处理在 C 后台完成,结果作为Python对象返回。在本文中,我们将看到如何使用名为SWIG的软件在 Linux 系统上为我们的 C 程序创建Python包装器。
什么是 SWIG
简而言之,SWIG 是一个编译器,它接受 C/C++ 声明并创建一个包装器,以便从其他语言(如Python、Tcl、Ruby 等)访问这些声明。
它通常不需要更改现有代码并在一分钟内创建一个界面。
创建包装器的原因
在许多情况下,我们需要包装器,以下是其中的几个——
- 为现有 C 程序构建解释接口。
- 为脚本语言构建高性能 C 模块
- 测试庞大的 C 程序非常痛苦,因此我们用一些脚本语言(如Python)编写包装器,在这些语言中编写测试非常容易。 ETC
安装 SWIG
要直接从 apt 存储库中下载 SWIG,请输入以下命令:
sudo apt-get update
sudo apt-get install swig
使用 SWIG 编写包装器
考虑这段 C 代码,有两个函数和一个全局变量——
/* file : gfg.c */
#include
#include
//our header file
#include "gfg.h"
#define ll long long
double myvar = 3.4;
// calculate factorial
ll int fact(ll int n)
{
if(n <= 1)
return 1;
else
return (n * fact(n-1));
}
//find mod
int my_mod(int n, int m)
{
return(n % m);
}
这是我们的头文件gfg.h –
long long int fact(long long int n);
int my_mod(int n, int m);
首先,我们必须创建一个SWIG 接口文件。该文件包含 ANSI C函数原型和变量声明。这里 -
- %module指令指定我们将在Python中使用的模块的名称。
- %{ .. % } 块提供了在生成的包装器代码中插入附加代码(例如 C 头文件或附加 C 声明)的位置。
- %include指令让我们包含附加文件,如头文件。
/* file : gfg.i */
/* name of module to use*/
%module gfg
%{
/* Every thing in this file is being copied in
wrapper file. We include the C header file necessary
to compile the interface */
#include "gfg.h"
/* variable declaration*/
double myvar;
%}
/* explicitly list functions and variables to be interfaced */
double myvar;
long long int fact(long long int n1);
int my_mod(int m, int n);
/* or if we want to interface all functions then we can simply
include header file like this -
%include "gfg.h"
*/
现在我们将使用$ swig -target_language interface_file.i之类的命令创建包装器代码
$ swig -python gfg.i
执行此命令后,将创建一个名为“gfg_wrap.c”的包装器代码。该文件包含我们原始 C 代码的臃肿版本以及各种错误处理代码等。生成另一个文件“gfg.py” ,这是我们将在Python脚本中导入的模块。
在此之后,我们必须通过使用以下命令编译“gfg_wrap.c”和“gfg.c”来生成将在共享库中使用的位置无关代码 -
$ gcc -c -fpic gfg_wrap.c gfg.c -I/use/include/python2.7
将 python2.7 替换为您的Python版本。这将生成两个目标文件
“gfg_wrap.o”和“gfg.o” 。在上面的命令中——
- -fpic生成适合在共享库中使用的与位置无关的代码 (PIC)(如果目标机器支持)。此类代码通过全局偏移表 (GOT) 访问所有常量地址
注意:如果您收到类似“... ' Python.h' file not found”之类的错误,则可能是以下原因 -
- 您可能没有“Python.h”文件或
- 您向编译器提供了错误的“Python.h”文件位置
要获得“Python.h”,您必须使用以下命令安装Python-dev –
$ sudo apt-get install python-dev
要找到“Python.h”的正确路径,请执行以下命令 -
$ python-config --cflags
这将输出如下内容 -
现在将编译命令中的路径替换为 python2.7 的路径,或者将Python 3.5 的版本更改为python3.5 。
现在,最后,我们必须将生成的对象文件链接在一起,以创建一个类似于 windows 中的dll文件的共享对象。使用以下命令,这将生成一个“_gfg.so”共享对象文件——
$ gcc -shared gfg.o gfg_wrap.o -o _gfg.so
现在我们准备通过导入来测试Python包装器。确保您位于具有此包装文件的目录中。
>>> import gfg
>>> res = fact(5)
>>> res
120
>>> res = my_mod(5,2)
>>> res
1
>>> gfg.cvar.myvar
3.4
这里 C 变量作为module.cvar.var_name访问。
使用 distutils 编译和链接
我们可以使用 distutils 自动执行此操作,而不是输入命令并找出编译文件所需的编译选项。创建一个setup.py如下 -
# File : setup.py
from distutils.core import setup, Extension
#name of module
name = "gfg"
#version of module
version = "1.0"
# specify the name of the extension and source files
# required to compile this
ext_modules = Extension(name='_gfg',sources=["gfg.i","gfg.c"])
setup(name=name,
version=version,
ext_modules=[ext_modules])
现在编写以下命令来编译和安装模块 -
$ python setup.py build_ext --inplace
它应该在终端上看起来像这样 -
可能的替代方案
显然,SWIG 不是创建包装器的唯一方法,可以根据他们的要求考虑以下替代方案 -
- 手动包装
- 耐热玻璃
- ctypes(内置模块)
- 啜
在下一篇文章中,我们将看到如何为 C++ 代码 (OPP) 创建包装器
参考
- http://www.swig.org/Doc3.0/Introduction.html