📜  Swig 包装 C 代码

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

Swig 包装 C 代码

先决条件:在Python中使用 C 代码,使用 SWIG 为Python包装 C/C++

假设我们已经给出了一个 C 代码,并且它需要作为一个 C 扩展模块来访问。因此,对于给定的任务——使用Swig Wrapper Generator。

Swig 通过解析 C 头文件并自动创建扩展代码来运行。首先需要 C 头文件,才能使用 Swig。在下面的代码中给出一个 C 头文件的例子。

代码#1: work.h

// work.h
  
# include 
  
extern int gcd(int, int);
extern int divide(int a, int b, int * remainder);
extern double avg(double * a, int n);
  
typedef struct Point
{
    double x, y;
} Point;
  
extern double distance(Point * p1, Point * p2);


有了头文件之后,下一步就是编写一个 Swig “接口”文件。按照惯例,这些文件具有.i后缀,可能类似于以下内容。

代码 #2: work.i

// work.i - Swig interface % module work %
{
# include "work.h"
    %
}
// Customizations % extend Point
{
    // Constructor for Point objects 
    Point(double x, double y)
    {
        Point * p = (Point *) malloc(sizeof(Point));
        p->x = x;
        p->y = y;
        return p;
    };
};


代码#3:映射

// Map int * remainder as an output argument
%include typemaps.i 
%apply int * OUTPUT { int * remainder };
   
// Map the argument pattern (double * a, int n) to arrays 
%typemap(in) (double * a, int n)(Py_buffer view)
{
    view.obj = NULL;
    if (PyObject_GetBuffer($input, &view, 
                           PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1)
    {
        SWIG_fail;
    }
    if (strcmp(view.format, "d") != 0)
    {
        PyErr_SetString(PyExc_TypeError, 
                        "Expected an array of doubles");
        SWIG_fail;
    }
    $1 = (double *) view.buf;
    $2 = view.len / sizeof(double);
}
   
% typemap(freearg) (double * a, int n)
{
    if (view$argnum.obj)
    {
        PyBuffer_Release(&view$argnum);
    }
}


接口文件准备好后,Swig 将作为命令行工具调用

代码#4:

bash % swig -python -py3 work.i
bash %


swig 的输出是两个文件work_wrap.cwork.py work.py文件是用户导入的文件, work_wrap.c文件是需要编译成名为_work的支持模块的 C 代码。它使用与普通扩展模块相同的技术来执行。例如,创建一个setup.py文件,如下面的代码所示 -

代码#5:

# setup.py
from distutils.core import setup, Extension
setup(name='sample', 
      py_modules=['sample.py'],
      ext_modules=[ Extension(
              '_sample', ['sample_wrap.c'], 
              include_dirs = [], 
              define_macros = [],
              undef_macros = [],
              library_dirs = [],
              libraries = ['sample']
              ) ] )


代码#6:编译测试,在setup.py上运行 python3

bash % python3 setup.py build_ext --inplace
running build_ext
building '_sample' extension
  
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
-I/usr/local/include/python3.3m -c work_wrap.c
-o build/temp.macosx-10.6-x86_64-3.3/work_wrap.o
  
work_wrap.c: In function ‘SWIG_InitializeModule’:
  
work_wrap.c:3589: warning: statement with no effect
  
gcc -bundle -undefined dynamic_lookup build/temp.macosx-10.6-x86_64-3.3/work.o
  
build/temp.macosx-10.6-x86_64-3.3/work_wrap.o -o _work.so -lwork
bash %


完成所有任务后,我们可以非常轻松地使用 C 扩展模块。

代码#7:

import work
print ("GCD : ", work.gcd(12,8))
  
print ("\nDivision : ", work.divide(42,8))
  
pt1 = work.Point(2,3)
pt2 = work.Point(4,5)
  
print ("\nDistance between pt1 and pt2 : ", 
       work.distance(pt1,pt2))
  
  
print ("\nx co-ordinate of pt1 : ", pt1.x)
print ("\ny co-ordinate of pt1 : ", pt1.x)
  
  
import array
ar = array.array('d',[2, 4, 6])
print ("\nAverage : ", work.avg(arr))

输出 :

GCD : 4

Divide : [5, 2]

Distance between pt1 and pt2 : 2.8284271247461903

Distance between pt1 and pt2 : 2.0

Distance between pt1 and pt2 : 3.0

Average : 4.0