📜  使用 SWIG 为Python包装 C/C++ – Set 1

📅  最后修改于: 2022-05-13 01:55:32.039000             🧑  作者: Mango

使用 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

它应该在终端上看起来像这样 -
setyp.py 输出

可能的替代方案

显然,SWIG 不是创建包装器的唯一方法,可以根据他们的要求考虑以下替代方案 -

  • 手动包装
  • 耐热玻璃
  • ctypes(内置模块)

在下一篇文章中,我们将看到如何为 C++ 代码 (OPP) 创建包装器

参考

  • http://www.swig.org/Doc3.0/Introduction.html