SWIG 之三:“11 Typemaps”

Posted kuliuheng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SWIG 之三:“11 Typemaps”相关的知识,希望对你有一定的参考价值。

原文地址: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;
}
View Code

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;
}
View Code

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 特殊变量

   typemap都能展开以下特殊变量:

VariableMeaning
$n A C local variable corresponding to type n in the typemap pattern.
$argnum Argument number. Only available in typemaps related to argument conversion
$n_name Argument name
$n_type Real C datatype of type n.
$n_ltype ltype of type n
$n_mangle Mangled form of type n. For example _p_Foo
$n_descriptor Type descriptor structure for type n. For exampleSWIGTYPE_p_Foo. This is primarily used when interacting with the run-time type checker (described later).
$*n_type Real C datatype of type n with one pointer removed.
$*n_ltype ltype of type n with one pointer removed.
$*n_mangle Mangled form of type n with one pointer removed.
$*n_descriptor Type descriptor structure for type n with one pointer removed.
$&n_type Real C datatype of type n with one pointer added.
$&n_ltype ltype of type n with one pointer added.
$&n_mangle Mangled form of type n with one pointer added.
$&n_descriptor Type descriptor structure for type n with one pointer added.
$n_basetype Base typename with all pointers and qualifiers stripped.

  其中n表示参数的索引值,第一个参数就是 $1,第二个参数就是$2。

  “ltype”的概念是指简写的合法左侧数据类型,例如:

type              ltype
------            ----------------
int               int
const int         int
const int *       int *
int [4]           int *
int [4][5]        int (*)[5]

 

11.4.4 特殊变量宏

  与普通宏不同,特殊宏的展开不是在预处理阶段完成,而是在SWIG解析/编译阶段完成。

11.4.4.1 $descriptor(type)

11.4.4.2 $typemap(type, typepattern)

  可以给特殊的静态语言(如Java、C#)来特定类型映射(分别是jtype、cstype)

11.4.5 特殊变量与typemap属性

11.4.6 特殊变量与特殊变量宏结合

11.5 常用的typemap方法

11.5.1 typemap(in)

  in 类型的typemap用于将函数参数从目标语言类型转换为C/C++类型。

11.5.2 typemap(typecheck)

  用于支持重载的函数和方法,检查一个参数是否与指定类型相匹配。

11.5.3 typemap(out)

  out 类型的typemap用于将函数/方法的返回值从C/C++类型转换成目标语言类型。

11.5.4 typemap(arginit)

  用于设置函数参数的初始值。一般不需要的。

11.5.5 typemap(default)

  用于更改可选默认参数的包装。对于不支持动态参数(如Java和C#)的语言则会忽略此指令,必须给出所有参数。

11.5.6 typemap(check)

  用于参数转换期间进行值检查。

11.5.7 typemap(argout)

  用于参数返回值,常用于需要返回多个值的C/C++函数包装。

11.5.8 typemap(freearg)

  用于清理 typemap(in) 代码中动态分配的资源,例如:

// Get a list of integers
%typemap(in) int *items {
  int nitems = Length($input);
  $1 = (int *) malloc(sizeof(int)*nitems);
}
// Free the list 
%typemap(freearg) int *items {
  free($1);
}

11.5.9 typemap(newfree)

  typemap(newfree) 与 %newobject 指令一起使用,用于释放函数返回结果所使用的内存。例如:

%typemap(newfree) string * {
  delete $1;
}
%typemap(out) string * {
  $result = PyString_FromString($1->c_str());
}
...

%newobject foo;
...
string *foo();

11.5.10 typemap(ret)

  不太常用,但对于与返回类型有关的内容则十分有用。

%typemap(ret) stringheap_t %{
  free($1);
%}

typedef char * string_t;
typedef char * stringheap_t;

string_t MakeString1();
stringheap_t MakeString2();

  这种方式完全可以替代上面的 typemap(newfree) 和 %newobject 结合的方式。这种是在数据类型上指定了内存自动释放逻辑。

11.5.11 typemap(memberin)

  用于已转换的输入值数据复制到结构成员中。通常用于处理数组。例如:

%typemap(memberin)int [4] {
  memmove($ 1,$ input,4 * sizeofint));
}

11.5.12 typemap(varin)

  用于将目标语言中的对象转换为C/C++全局变量

11.5.13 typemap(varout)

  用于读取C/C++全局变量时转换成目标语言的对象。

11.5.14 typemap(throw)

  异常处理。

11.6 一些typemap示例

11.6.1 数组的typemap

 

 

 

 

 

  

 


以上是关于SWIG 之三:“11 Typemaps”的主要内容,如果未能解决你的问题,请参考以下文章

极智开发 | 讲解 React 组件三大属性之三:refs

时间序列专题之三 时间序列的分段线性表示

时间序列专题之三 时间序列的分段线性表示

用Swig将c/c++程序转为java代码(使用swig实现java调用cc++的方法)

swig 生成的代码链接到错误的 python 安装

将代码添加到 SWIG 中自动生成的类