阿酷尔工作室

V1

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

SWIG教程《二》

变量属性

使用immutable创建只读字段,使用该字段编辑的变量在对应目标语言中只有对应的GetX接口,无法对值进行修改。

一个变量一旦被标记为immutable之后,只有明确再次指定为mutable,否则一直是只读变量

The %immutable directive enables read-only mode until it is explicitly disabled using the %mutable directive

// File : interface.i
int a; // Can read/write
%immutable;
int b, c, d; // Read only variables
%mutable;
double x, y; // read/write

Compatibility note: Read-only access used to be controlled by a pair of directives %readonly and %readwrite. Although these directives still work, they generate a warning message.

Simply change the directives to %immutable; and %mutable; to silence the warning. Don't forget the extra semicolon

重命名

有时候将对应语言转换为目标语言时,由于关键字的不同,在该语言中能正常使用的变量在目标语言中可能是关键字,这个时候可以使用rename进行重命名。

// interface.i
// 将源语言中的print函数重命名为my_print这样在目标语言中就可以通过my_print来调用源语言中的print
%rename(my_print) print;
extern void print(const char *);
%rename(foo) a_really_long_and_annoying_name;
extern int a_really_long_and_annoying_name;

rename作用域从开始声明一直到文件结束,所以如果想那个头文件中被重命名需要保证该头文件的引入在rename之后

%rename(my_print) print;
%rename(foo) a_really_long_and_annoying_name;
%include "header.h"
// 双引号添加不添加都无所谓,但是当重命名的名字是C++的关键字是,添加双引号就成为了必须的了
%rename("foo_short") foo(short);
%rename(foo_long) foo(long);
void foo(int);
void foo(short)// Accessed as foo_short()
void foo(long)// Accessed as foo_long()

能够对单个命名通常已经够用了,但swig的功能远不止于此,可以通过匹配进行批量命名,如为所有的函数增加前缀

%rename("myprefix_%s"""// print -> myprefix_print

将所有蛇形命名的方式修改为驼峰命名的方式

%rename("%(lowercamelcase)s"""// foo_bar -> fooBar; FooBar -> fooBar

剔除一些命名中的字符在使用

%rename("%(strip:[wx])s"""// wxHello -> Hello; FooBar -> FooBar

对操作符重载函数进行重命名

%rename(__add__) Complex::operator+;

如果操作符重载是按照友元函数或者全局函数重载的需要按照如下方式进行重命名

%rename(op_j) ::operator+;

同时也支持使用正则进行匹配

// Strip the wx prefix from all identifiers except those starting with wxEVT
%rename("%(regex:/wx(?!EVT)(.*)/\\1/)s"""// wxSomeWidget -> SomeWidget
// wxEVT_PAINT -> wxEVT_PAINT
// Apply a rule for renaming the enum elements to avoid the common prefixes
// which are redundant in C#/Java
%rename("%(regex:/^([A-Z][a-z]+)+_(.*)/\\2/)s", %$isenumitem) ""// Colour_Red -> Red
// Remove all "Set/Get" prefixes.
%rename("%(regex:/^(Set|Get)(.*)/\\2/)s"""// SetValue -> Value
// GetValue -> Value

Ignore

使用ignore可以指示swig忽略一些不需要封装的内容

%ignore print; // Ignore all declarations named print
%ignore MYMACRO; // Ignore a macro
...
#define MYMACRO 123
void print(const char *);

可以选择忽略大多数符号,只对其中一小部分进行封装

Using the techniques described above it is possible to ignore everything in a header and then selectively wrap a few chosen methods or classes. For example, consider a header, myheader.h

which has many classes in it and just the one class called Star is wanted within this header, the following approach could be taken:

%ignore ""// Ignore everything
// Unignore chosen class 'Star'
%rename("%s") Star;
// As the ignore everything will include the constructor, destructor, methods etc
// in the class, these have to be explicitly unignored too:
%rename("%s") Star::Star;
%rename("%s") Star::~Star;
%rename("%s") Star::shine; // named method
%include "myheader.h"

同样可以按照类型进行忽略

%rename($ignore, %$isclass) ""// Only ignore all classes
%rename("%s") Star; // Unignore 'Star'
%include "myheader.h"

回调函数

一些需要传入回调的函数,需要提前声明,不能将目标函数实现的函数直接传入

/* Function with a callback */
int binary_op(int a, int b, int (*op)(intint));
/* Some callback functions */
// 要将将要使用的回调函数传入
%constant int add(intint);
%constant int sub(intint);
%constant int mul(intint);

一旦声明为回调函数,将不能在目标函数中被当成普通函数使用,如果一个函数即当成回调函数也想当成普通函数调用,可以按照如下方式进行声明

/* Function with a callback */
int binary_op(int a, int b, int (*op)(intint));
/* Some callback functions */
%callback("%s_cb");
int add(intint);
int sub(intint);
int mul(intint);
%nocallback;

构造与析枸

Swig 为每个C++的类生成默认的构造函数和析枸函数,如果不需要可以使用参数选项或者指令进行禁止

If you don't want SWIG to generate default constructors for your interfaces, you can use the %nodefaultctor directive or the -nodefaultctor command line option. For example:

swig -nodefaultctor example.i

Or

%module foo
...
%nodefaultctor; // Don't create default constructors
... declarations ...
%clearnodefaultctor; // Re-enable default constructors

或者你可以更加精确的指定那个类不创建构造函数

%nodefaultctor Foo; // No default constructor for Foo
...
struct Foo { // No default constructor generated.
};
struct Bar { // Default constructor generated.
};

虽然,不创建析枸函数往往会导致内存泄露,但是swig还是允许你控制不创建析枸函数通过nodefaultdtor指定不需要析枸函数的类型

%nodefaultdtor Foo; // No default/implicit destructor for Foo
...
struct Foo { // No default destructor is generated.
};
struct Bar { // Default destructor generated.
};

Note: There are also the-nodefault option and %nodefault directive, which disable both the default or implicit destructor generation. This could lead to memory leaks across the target

languages, and it is highly recommended you don't use them

限制

虽然siwg能够处理大多数的C++语法,但swig并不是完整的C++解析器,仍然有很多的语法是swig无法解析的。

  • Non-conventional type declarations. For example, SWIG does not support declarations such as the following (even though this is legal C):
/* Non-conventional placement of storage specifier (extern) */
const int extern Number;
/* Extra declarator grouping */
Matrix (foo); // A global variable
/* Extra declarator grouping in parameters */
void bar(Spam (Grok)(Doh));

为C++的类添加成员函数

很多C类型的结构体是不能绑定成员函数的,但是其他函数中都能,为了提供便利swig支持为C类型的结构体绑定额外的成员函数用来辅助结构体的创建和使用,这时就要用到%extend指令了

/* file : vector.h */
...
typedef struct Vector {
    double x, y, z;
} Vector;

通过extend指令为C++类型的结构体添加构造函数

// file : vector.i
%module mymodule
%{
#include "vector.h"
%}
%include "vector.h" // Just grab original C header file
%extend Vector { // Attach these functions to struct Vector
    Vector(double x, double y, double z) {
        Vector *v;
        v = (Vector *) malloc(sizeof(Vector));
        v->x = x;
        v->y = y;
        v->z = z;
        return v;
    }
    ~Vector() {
        free($self);
    }
    double magnitude() {
        return sqrt($self->x*$self->x+$self->y*$self->y+$self->z*$self->z);
    }
    void print() {
        printf("Vector [%g, %g, %g]\n", $self->x, $self->y, $self->z);
    }
};

但是extend指令只能用于struct上,不能用于经过typedef之后的类型名

typedef struct Integer {
int value;
} Int;
%extend Integer { ... } /* Correct name */
%extend Int { ... } /* Incorrect name */
struct Float {
float value;
};
typedef struct Float FloatValue;
%extend Float { ... } /* Correct name */
%extend FloatValue { ... } /* Incorrect name */

有一种特殊情况除外,那就是当typedef一个匿名类时,是可以使用extend的

typedef struct {
double value;
} Double;
%extend Double { ... } /* Okay */

代码注入

swig代码输出格式

swig输出的C/C++代码会分为5部分

  • Begin section.

A placeholder for users to put code at the beginning of the C/C++ wrapper file. This is most often used to define preprocessor macros that are used in later sections.

  • Runtime code.

This code is internal to SWIG and is used to include type-checking and other support functions that are used by the rest of the module.

  • Header section.

This is user-defined support code that has been included by the %{ ... %} directive. Usually this consists of header files and other helper functions.

  • Wrapper code.

These are the wrappers generated automatically by SWIG.

  • Module initialization.

The function generated by SWIG to initialize the module upon loading.

代码注入指令

命令注入格式:

// 将在section部分注入代码,section为begin runtime header wrapper init等字段
%insert("section""filename"
// 将后面代码段的内容注入到指定段
%insert("section") %{ ... %}

当然除了使用insert指令,section名本身也是一种指令,也可以section作为指令的方式来对指定代码段进行代码注入

For example, %runtime is used instead of %insert("runtime").

%begin %{
... code in begin section ...
%}
%runtime %{
... code in runtime section ...
%}
%header %{
... code in header section ...
%}
%wrapper %{
... code in wrapper section ...
%}
%init %{
... code in init section ...
%}

说明:其实经常使用的%{....%},其实是%header %{...%}的简写

The bare %{ ... %} directive is a shortcut that is the same as %header %{ ... %}

Init 段是给有些需要进行初始化的代码使用的。

辅助函数

实际使用中经常使用代码注入的功能来写辅助函数

%{
    /* Create a new vector */
    static Vector *new_Vector() {
        return (Vector *) malloc(sizeof(Vector));
    } 
%}
// Now wrap it
Vector *new_Vector();

为了简化辅助代码的书写,引入了inline指令,该指令使用方式如下:

凡是被inline标记的代码段,相当于在代码和封装中都手写了一遍的效果:

%inline %{
/* Create a new vector */
Vector *new_Vector() {
return (Vector *) malloc(sizeof(Vector));
}
%}

等效于

%{
/* Create a new vector */
Vector *new_Vector() {
return (Vector *) malloc(sizeof(Vector));
}%
}
/* Create a new vector */
Vector *new_Vector() {
return (Vector *) malloc(sizeof(Vector));
}

但是需要注意的是,inline只是对函数和变量有效,对于头文件的引用是否封装,还是需要原有的方式,在代码块中使用#include 引入头文件,而需要封装成目标代码的头文件需要在代码块外使用%include指令来告知swig需要对该头文件进行封装

说明:编译选项-Dmain=oldmain其实就是宏定义替换过程中使用,高速预处理器将main更换成等号后面的符号

分类:

后端

标签:

后端

作者介绍

阿酷尔工作室
V1

恒生研究院