原文地址:《11 Typemaps》
11.1 简介
typemaps 是SWIG中的一种高级定制功能, 可以直接指定代码包装转换的底层行为。
11.1.1 类型转换
在SWIG代码包装生成中最重要的问题之一就是不用语言之间的数据类型转换。
11.1.2 typemaps
使用 %typemap 指令来指示转换代码行为,in表示从目标语言到C/C++,out表示从C/C++到目标语言:
/* Convert from Python --> C */ %typemap(in) int { $1 = PyInt_AsLong($input); } /* Convert from C --> Python */ %typemap(out) int { $result = PyInt_FromLong($1); }
扩展了许多以$前缀的特殊变了用来应对复杂语言(如Java)的转换,$input 表示需要转换成C/C++的输入对象,$result 表示包装函数的返回对象,$1表示一个C/C++变量。举个例子:
int gcd(int x,int y);
对应包装内容可能是这样的:
PyObject *wrap_gcd(PyObject *self, PyObject *args) { int arg1; int arg2; int result; PyObject *obj1; PyObject *obj2; PyObject *resultobj; if (!PyArg_ParseTuple("OO:gcd", &obj1, &obj2)) return NULL; /* "in" typemap, argument 1 */ { arg1 = PyInt_AsLong(obj1); } /* "in" typemap, argument 2 */ { arg2 = PyInt_AsLong(obj2); } result = gcd(arg1, arg2); /* "out" typemap, return value */ { resultobj = PyInt_FromLong(result); } return resultobj; }
11.1.3 模式匹配
typemap支持typedef 改名操作。
11.1.4 复用typemaps
用 %typemap 可以指定某些类型的行为与已知类型行为一致:
%typemap(in) Integer = int; %typemap(in) (char *buffer, int size) = (char *str, int len);
其实还可以用 %apply 更简洁:
%typemap(in) int { /* Convert an integer argument */ ... } %typemap(out) int { /* Return an integer value */ ... } /* Apply all of the integer typemaps to size_t */ %apply int { size_t }; // 花括号里面还可以用逗号分隔多种数据类型的转换,都转成int的行为
如果已经用 typedef int size_t; 操作指定了改名,就不需要再用上面的指令来转换了。
11.1.5 使用typemaps可以干什么?
主要作用是用来定义C/C++层包装代码的生成行为。目前可以解决六大类问题:
(1)参数处理
- %typemap(in) 输入参数转换
- %typemap(typecheck) 输入参数类型检查重载方法中使用的类型
- %typemap(argout) 输出参数处理
- %typemap(check) 输入参数值检查
- %typemap(arginit) 输入参数初始化
- %typemap(default) 默认参数
- %typemap(freearg) 输入参数资源管理
(2)返回值处理
- %typemap(out) 函数返回值转换
- %typemap(ret) 返回值资源管理(“ret”类型映射)
- %typemap(newfree) 新分配对象的资源管理(“newfree”typemap)
(3)异常处理
- %typemap(throw) 处理C ++异常规范
(4)全局变量
- %typemap(varin) 分配全局变量
- %typemap(varout) 读取全局变量
(5)成员变量
- %typemap(memberin) 将数据分配给类/结构成员
(6)常量定义
- %typemap(consttab / constcode) 定义常量
11.1.6 使用typemaps 不能干什么?
(1)不能通过函数返回值来区分一个特性,例如:
Foo *make_Foo(int n);
要想实现这一点可以借助 %feature 来实现。
(2)不能改变参数的调用顺序,例如:
void foo(int, char *);
要想实现参数的转换可以定义辅助函数:
%rename(foo) wrap_foo; %inline %{ void wrap_foo(char *s, int x) { foo(x, s); } %}
11.1.7 与面向对象编程的相似点
11.1.8 本章附录
11.2 Typemap规范
11.2.1 定义typemap
%typemap(method [, modifiers]) typelist code ;
- method 指定typemap类型,前面括号里的“in”、“out”之类的字符串就是。
- modifiers 是name="value" 可称作typemap属性,附加一些额外信息,与目标语言有关。可选。
- typelist 是C++类型模式匹配表。
- code 是typemap中使用的代码,通常是C/C++代码,也可以是目标语言代码。
其中 typelist 可以用逗号分隔写多个匹配模式,规则如下:
typelist : typepattern [, typepattern, typepattern, ... ] ; typepattern : type [ (parms) ] | type name [ (parms) ] | ( typelist ) [ (parms) ]
而 code 则可以有三种形式可选:
code : { ... } | " ... " | %{ ... %}
来举几个例子:
/* 简单的typemap示例 */ %typemap(in) int { $1 = PyInt_AsLong($input); } %typemap(in) int "$1 = PyInt_AsLong($input);"; %typemap(in) int %{ $1 = PyInt_AsLong($input); %} /* 带有参数名称的typemap */ %typemap(in) int nonnegative { ... } /* 支持多种类型的typemap */ %typemap(in) int, short, long { $1 = SvIV($input); } /* 带有modifiers的typemap */ %typemap(in, doc="integer") int "$1 = scm_to_int($input);"; /* 支持多参数模式的typemap */ %typemap(in) (char *str, int len), (char *buffer, int size) { $1 = PyString_AsString($input); $2 = PyString_Size($input); } /* 带有额外模式参数的typemap */ %typemap(in, numinputs=0) int *output (int temp), long *output (long temp) { $1 = &temp; }
11.2.2 类型映射范围
一个类型映射定义对后面所有的声明都生效。直至下一个同类型的typemap定义为止。
%extend 扩展类/结构体定义不受 %typemap 影响。
11.2.3 复制一个typemap
用赋值等号就可以拷贝,前面其实也说过了:
%typemap(in) Integer = int; %typemap(in) Integer, Number, int32_t = int; // 多个类型都遵循int
通常我们会针对某个数据类型声明多种类型的typemap,那么可以用 %apply 指令来批量拷贝这些typemap :
%apply int { Integer }; // Copy all int typemaps to Integer %apply int { Integer, Number }; // Copy all int typemaps to both Integer and Number %apply int *output { Integer *output }; // 遵循%typemap 一样的匹配模式 %apply (char *buf, int len) { (char *buffer, int size) }; // 多参数模式的匹配
11.2.4 删除一个typemap
清除指定类型的typemap就是没有code的重新声明:
%typemap(in) int; // Clears typemap for int %typemap(in) int, long, short; // Clears typemap for int, long, short %typemap(in) int *output;
用 %clear 指令可以清除指定类型的所有typemap:
%clear int; // Removes all types for int %clear int *output, long *output;
请注意:用了%clear 清除了指定类型typemap之后会把SWIG默认的转换逻辑也清除了,会使得该类型不可用了,所以一般都需要立即重新声明一套新规则。
11.2.5 typemaps的声明位置
可以全局声明、C++ 命名空间内声明,也可以在C++类内部声明。命名空间内的声明仅针对该空间内的类型。
11.3 模式匹配规则
遵循以下优先级:
- 类型和名称完全匹配
- 类型匹配
- 对于C++模板 T<TPARAMS> 会删除模板参数类型,然后依次检查:T和NAME完全匹配、仅与T完全匹配
举个例子:
int foo(const char * s); // 要查找const char * 参数类型映射,SWIG将依次搜索以下类型映射: const char *s Exact type and name match const char * Exact type match char *s Type and name match (qualifier stripped) char * Type match (qualifier stripped)
如果是一个数组,可以用 ANY 关键字来匹配任意长度。
11.3.2 typedef 精简匹配
SWIG优先遵循上述顺序匹配参数类型,如果都找不到,就按照typedef的替换关系来依次查找,比如说有以下定义:
typedef int Integer; typedef Integer Row4[4]; void foo(Row4 rows[10]);
那么在匹配参数 Row4 rows[10] 的时候遵循以下查找顺序:
Row4 rows[10] Row4 [10] Row4 rows[ANY] Row4 [ANY] # Reduce Row4 --> Integer[4] Integer rows[10][4] Integer [10][4] Integer rows[ANY][ANY] Integer [ANY][ANY] # Reduce Integer --> int int rows[10][4] int [10][4] int rows[ANY][ANY] int [ANY][ANY]
参数的匹配替换规则是从左至右,把最左边的参数通过typedef替换后再替换右边的参数,所以如果左边用了typedef而右边没有typedef的typedef声明永远都不会被匹配到,SWIG并不会搜索所有可能的typedef。
11.3.3 默认的typemap 匹配规则
???
11.3.4 多参数类型映射
优先匹配多参数的模板。
11.3.5 与C++模板的匹配规则对比
11.3.6 调试typemap映射模式匹配
11.4 代码生成规则
11.4.1 范围
11.4.2 声明新的局部变量
11.4.3 特殊变量