阿酷尔工作室

V1

2022/08/10阅读:28主题:默认主题

SWIG教程《一》

Swig《一》

指令文件说明

不同语言之间转换最主要的就是不同语言之间语法的封装,swig就是用来干这些事情的。而这些字段正是指导swig来完成这些操作的指令

/* File : example.i */
%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);

module:指定swig会创建的模块

%module mymodule
%{
// 这里是源代码中需要的
#include "myheader.h"
%}
// 这里是转换之后目标代码中需要的
// Now list ISO C/C++ declarations
int foo;
int bar(int x);
...

%{....%}

  • 所有在%{....%}之间的内容会被一字不差的放置到被swig创建的封装的文件中。这个区域通常用来放置一些swig不会生成的定义或者声明,对应C/C++的封装通常用来引入头文件

%+指令

  • 大多数的swig指令都是这种以%开头的指令,目的就是为了能和C能够很好的区分开来

  • 默认情况下,swig是不会对include进来的头文件中的#include进行展开的,除非你指定-includeall

%{
/* Include in the generated wrapper file */
typedef unsigned int size_t;
%}
/* Tell SWIG about it */
typedef unsigned int size_t;

Or

%inline %{
typedef unsigned int size_t;
%}

像C语言一样,swig中也能使用swig进行类型重定义

%include 引入头文件

使用%include指令来引入swig定义的头文件

%import引入头文件

如果只是想引入文件使用文件中的宏定义和类型可以使用import

%import "foo.i"

预定义变量

为了区分不同语言以及在部分指导文件中能够区别进行,siwg中提前预定义了一些变量

SWIG Always defined when SWIG is processing a file
SWIGIMPORTED Defined when SWIG is importing a file with %import
SWIG_VERSION Hexadecimal (binary-coded decimal) number containing SWIG version,
such as 0x010311 (corresponding to SWIG-1.3.11).
SWIGCSHARP Defined when using C#
SWIGD Defined when using D
SWIGGO Defined when using Go
SWIGGUILE Defined when using Guile
SWIGJAVA Defined when using Java
SWIGJAVASCRIPT Defined when using Javascript
SWIG_JAVASCRIPT_JSC Defined when using Javascript with -jsc
SWIG_JAVASCRIPT_V8 Defined when using Javascript with -v8 or -node
SWIGLUA Defined when using Lua
SWIGMZSCHEME Defined when using Mzscheme
SWIGOCAML Defined when using OCaml
SWIGOCTAVE Defined when using Octave
SWIGPERL Defined when using Perl
SWIGPHP Defined when using PHP (any version)
SWIGPHP7 Defined when using PHP7
SWIGPYTHON Defined when using Python
SWIGR Defined when using R
SWIGRUBY Defined when using Ruby
SWIGSCILAB Defined when using Scilab
SWIGTCL Defined when using Tcl
SWIGXML Defined when using XML

__LINE__ Current line number
__FILE__ Current file name
__STDC__ Defined to indicate ISO C
__cplusplus Defined when -c++ option used

SWIG_D_VERSION Unsigned integer target version when using D
SWIGGO_CGO Defined when using Go for cgo
SWIGGO_GCCGO Defined when using Go for gccgo
SWIGGO_INTGO_SIZE Size of the Go type int when using Go (32 or 64)
SWIGPYTHON_PY3 Defined when using Python with -py3
SWIGPYTHON_BUILTIN Defined when using Python with -builtin
SWIG_RUBY_AUTORENAME Defined when using Ruby with -autorename

增强型的宏定义

为了更好的实现对源码的封装,swig提供了一种增强型的宏定义指令, %define 和 %enddef

%define ARRAYHELPER(type, name)
%inline %
{
type *new_ ## name (int nitems) {
return (type *) malloc(sizeof(type)*nitems);
}
void delete_ ## name(type *t) {
free(t);
}
type name ## _get(type *t, int index) {
return t[index];
}
void name ## _set(type *t, int index, type val) {
t[index] = val;
}
%}
%enddef
ARRAYHELPER(int, IntArray)
ARRAYHELPER(double, DoubleArray)

指令透传

正常情况下,在需要封装的函数中,swig会将#define进行处理,如果想让对应的宏定义不进行预处理可以在#前面添加%来实现

%extend Foo {
void bar() {
    %#ifdef DEBUG
    printf("I'm in bar\n");
    %#endif
}
}

指针处理(cpointer.i)

在一些函数中需要传入指针,但是在其他函数中可能没有指针这个概念,因此swig特意为指针提供了封装方式%pointer_functions(int**, intp);的含义就是,提供一个函数封装,这个函数封装可以创建指针执行的内容,这里的含义就是申请一块int大小的内存并返回

%include "cpointer.i"
/* Create some functions for working with "int *" */
%pointer_functions(int, intp);
/* A function that uses an "int *" */
void add(int x, int y, int *result);

如果想处理复杂的或者自定义的class可以使用%pointer_class(int, intp);来进行处理,相比之下使用pointer_class将申请的内容当成一个对象来管理更加的便捷和方便垃圾回收

%module example
%include "cpointer.i"
/* Wrap a class interface around an "int *" */
%pointer_class(int, intp);
/* A function that uses an "int *" */
void add(int x, int y, int *result);

类型转换

当一些类型需要转换是可以使用类型转换指令pointer_cast,第一个参数是当前类型,第二个参数是转换之后的参数,第三个是封装的函数名字

%include "cpointer.i"
%pointer_cast(int *, unsigned int *, int_to_uint);

数组(carrays.i)

使用前先引入头文件,指令方式%array_functions(type, name)

%include "carrays.i"
%array_functions(double, doubleArray);
void print_array(double x[10]);

同样,这里可以使用array_class来封装自定义类型的数组

%module example
%include "carrays.i"
%array_class(double, doubleArray);
void print_array(double x[10]);

内存管理(cmalloc.i)

swig提供了内存管理的模块,使用这些指令可以对malloc,calloc,realloc和free进行封装,具体的指令形式如下

%malloc(type [, name=type])
%calloc(type [, name=type])
%realloc(type [, name=type])
%free(type [, name=type])
%sizeof(type [, name=type])
%allocators(type [, name=type])
// SWIG interface
%include "cmalloc.i"
%malloc(int);
%free(int);
%malloc(int *, intp);
%free(int *, intp);
%allocators(double);

C类型数据封装(cdata.i)

引入cdata.i模块后,会在封装代码中提供Cdata*, memmove()等函数的封装用于申请和释放C类型数据。

如果类型已知可以使用以下方式

%include "carrays.i"
%include "cdata.i"
%array_class(int, intArray);

如果类型是未知类型,就需要借助cdata指令了%cdata(type [, name=type])

内存释放

当一个C中的函数申请一块内存,并返回一个char *类型时,swig可能会判断不出对应的内容是否需要释放,新版本的swig已经能够对简单的源码进行自行判断,如果源码中是申请了一块内存但是没有释放,在目标代码中swig会默认封装上释放的函数,比如go中没有char * 类型的数据,在进行封装时swig会创建一个string类型数据,并将原有的char *内容赋值到string对象中,然后根据判断自动释放对象,但是函数复杂时就需要进行人为指定了,这时需要用到指令%newobject来进行指定

%newobject foo;
char *foo() {
    char *result = (char *) malloc(sizeof(char));
    return result;
}

STL的支持

对于STL的支持是一个循序渐进的过程,目前已经支持的STL如下所示

STL
STL

使用STL时需要注意异常的抛出,通常在使用时需要对可能发生的异常进行适当的封装

%module example
%include "std_vector.i"
%typemap(throws) std::out_of_range {
// custom exception handler
}
%template(VectInt) std::vector<int>;

更加通用的方法是对所有异常进行封装

%include "exception.i"
%exception {
    try {
        $action
    } catch (const std::exception& e) {
        SWIG_exception(SWIG_RuntimeError, e.what());
    }
}

SWIG_exception(int code, const char *message)用于在封装的目标函数代码中抛出异常,常见的错误类型如下

SWIG_MemoryError
SWIG_IOError
SWIG_RuntimeError
SWIG_IndexError
SWIG_TypeError
SWIG_DivisionByZero
SWIG_OverflowError
SWIG_SyntaxError
SWIG_ValueError
SWIG_SystemError

参数类型(typemaps.i)

假如有以下函数,如果不对result进行声明,那么swig只会对按照指针的形式处理result参数,如果你直接传入一个空的result底层会在赋值时直接崩溃

void add(double a, double b, double *result) {
    *result = a + b;
}

因此需要使用%apply指令来对参数进行指导,对参数添加上指令%apply double *OUTPUT之后,swig已经知道result是一个需要输出的参数,因此会对result的合法性进行检测,如果是异常值(null)那么直接抛出异常

%include "typemaps.i"
%apply double *OUTPUT { double *result };
%inline %{
extern void add(double a, double b, double *result);
extern void Sum(double a, double b, double *result);
%}

如果有多个参数需要输出,只需要按照列表的形式向后添加即可

%include "typemaps.i"
%apply int *OUTPUT { int *width, int *height };
// Returns a pair (width, height)
void getwinsize(int winid, int *width, int *height);

如果你嫌每次都要写一长串的指令比较麻烦可以将以上过程简化成如下方式,直接将对应的参数更换成OUTPUT即可

%include "typemaps.i"

%inline %{
extern void add(double a, double b, double *OUTPUT);
%}

另外需要注意的是,一旦使用%apply进行声明,那么后续所有的double *result参数都会被处理,如果想取消%apply的作用需要进行如下声明:

%clear double *result; // Remove all typemaps for double *result

我们更常用的形式是:

入参

int *INPUT
short *INPUT
long *INPUT
unsigned int *INPUT
unsigned short *INPUT
unsigned long *INPUT
double *INPUT
float *INPUT

出参

int *OUTPUT
short *OUTPUT
long *OUTPUT
unsigned int *OUTPUT
unsigned short *OUTPUT
unsigned long *OUTPUT
double *OUTPUT
float *OUTPUT

即是入参又是出参

int *INOUT
short *INOUT
long *INOUT
unsigned int *INOUT
unsigned short *INOUT
unsigned long *INOUT
double *INOUT
float *INOUT

以上书写方式,直接放到参数上,等同使用apply的如下指令形式

// Make double *result an output value
%apply double *OUTPUT { double *result };
// Make Int32 *in an input value
%apply int *INPUT { int32 *in };
// Make long *x inout
%apply long *INOUT {long *x};
...
%clear double *result;
%clear Int32 *in, long *x;

分类:

后端

标签:

后端

作者介绍

阿酷尔工作室
V1

恒生研究院