目錄
- 11 型別映射
- 11.1 引言
- 11.1.1 型別轉換
- 11.1.2 型別映射
- 11.1.3 模式匹配
- 11.1.4 復用型別映射
- 11.1.5 型別映射能干什么?
- 11.1.6 型別映射不能干什么?
- 11.1.7 與面向切面編程的相似之處
- 11.1.8 本章的剩余部分
- 11.2 型別映射詳述
- 11.2.1 定義一個型別映射
- 11.2.2 型別映射作用范圍
- 11.2.3 復制型別映射
- 11.2.4 洗掉型別映射
- 11.2.5 型別映射的位置
- 11.3 模式匹配規則
- 11.3.1 基本匹配規則
- 11.3.2 typedef 還原匹配
- 11.3.3 默認型別映射匹配規則
- 11.3.4 多引數型別映射
- 11.3.5 匹配規則對比 C++ 模板
- 11.3.6 除錯型別映射模式匹配
- 11.4 代碼生成規則
- 11.4.1 作用域
- 11.4.2 宣告新的區域變數
- 11.4.3 特殊變數
- 11.4.4 特殊變數宏
- 11.4.4.1 $descriptor(type)
- 11.4.4.2 $typemap(method, typepattern)
- 11.4.5 特殊變數與型別映射屬性
- 11.4.6 特殊變數聯合特殊變數宏
- 11.5 通用型別映射方法
- 11.5.1 in 型別映射
- 11.5.2 typecheck 型別映射
- 11.5.3 out 型別映射
- 11.5.4 arginit 型別映射
- 11.5.5 default 型別映射
- 11.5.6 check 型別映射
- 11.5.7 argout 型別映射
- 11.5.8 freearg 型別映射
- 11.5.9 newfree 型別映射
- 11.5.10 ret 型別映射
- 11.5.11 memberin 型別映射
- 11.5.12 varin 型別映射
- 11.5.13 varout 型別映射
- 11.5.14 throws 型別映射
- 11.6 一些型別映射示例
- 11.6.1 陣列的型別映射
- 11.6.2 用型別映射的實作限制
- 11.7 多目標語言的型別映射
- 11.8 回傳值時的最優代碼生成
- 11.9 多引數型別映射
- 11.10 型別映射警告
- 11.11 型別映射片段
- 11.11.1 片段型別特化
- 11.11.2 片段與自動型別映射特化
- 11.12 運行時型別檢查器
- 11.12.1 實作
- 11.12.2 使用
- 11.13 型別映射與多載
- 11.14 %apply 和 %clear 詳情
- 11.15 在型別映射間傳遞資料
- 11.16 C++ this 指標
- 11.17 到哪去找更多的資訊?
- 11.1 引言
11 型別映射
11.1 引言
Chances are, you are reading this chapter for one of two reasons; you either want to customize SWIG's behavior or you overheard someone mumbling some incomprehensible drivel about "typemaps" and you asked yourself "typemaps, what are those?" That said, let's start with a short disclaimer that "typemaps" are an advanced customization feature that provide direct access to SWIG's low-level code generator. Not only that, they are an integral part of the SWIG C++ type system (a non-trivial topic of its own). Typemaps are generally not a required part of using SWIG. Therefore, you might want to re-read the earlier chapters if you have found your way to this chapter with only a vague idea of what SWIG already does by default.
你正在閱讀本章的原因可能有兩個:你想自定義 SWIG 的行為,或是無意中聽到有人抱怨“typemaps”一詞,并問自己“typemaps 是什么?”,那么,讓我們從一個簡短的免責宣告開始,即“typemaps”是一種高級定制功能,可以直接訪問 SWIG 的低級代碼生成器,不僅如此,它們還是 SWIG C++ 型別系統(SWIG 自身的重要內容)的組成部分,通常,不是使用 SWIG 的必需部分,因此,如果你閱讀本章時對 SWIG 默認情況下的行為認識模糊,那么你可能想重新閱讀前面的章節,
11.1.1 型別轉換
One of the most important problems in wrapper code generation is the conversion or marshalling of datatypes between programming languages. Specifically, for every C/C++ declaration, SWIG must somehow generate wrapper code that allows values to be passed back and forth between languages. Since every programming language represents data differently, this is not a simple of matter of simply linking code together with the C linker. Instead, SWIG has to know something about how data is represented in each language and how it can be manipulated.
To illustrate, suppose you had a simple C function like this:
包裝器代碼生成中最重要的問題之一是編程語言之間資料型別的轉換或編組,具體來說,對于每個 C/C++ 宣告,SWIG 必須以某種方式生成包裝器代碼,該包裝器代碼允許在語言之間來回傳遞值,由于每種編程語言表示資料的方式都不相同,因此簡單地將代碼與 C 聯結器鏈接在一起并不是一件容易的事,相反,SWIG 必須了解每種語言如何表示資料,以及如何對其進行操縱的知識,
為了說明這一點,假設你有一個簡單的 C 函式,如下所示:
int factorial(int n);
To access this function from Python, a pair of Python API functions are used to convert integer values. For example:
要從 Python 訪問此函式,要用大一對 Python API 函式轉換整數值,例如:
long PyInt_AsLong(PyObject *obj); /* Python --> C */
PyObject *PyInt_FromLong(long x); /* C --> Python */
The first function is used to convert the input argument from a Python integer object to C long. The second function is used to convert a value from C back into a Python integer object.
Inside the wrapper function, you might see these functions used like this:
第一個函式用于將輸入引數從 Python 整數物件轉換為 C 中的
long,第二個函式用于將值從 C 轉換回 Python 整數物件,在包裝器函式中,你可能會看到這些函式的用法如下:
PyObject *wrap_factorial(PyObject *self, PyObject *args) {
int arg1;
int result;
PyObject *obj1;
PyObject *resultobj;
if (!PyArg_ParseTuple("O:factorial", &obj1)) return NULL;
arg1 = PyInt_AsLong(obj1);
result = factorial(arg1);
resultobj = PyInt_FromLong(result);
return resultobj;
}
Every target language supported by SWIG has functions that work in a similar manner. For example, in Perl, the following functions are used:
SWIG 支持的每種目標語言都具有以類似方式作業的函式,例如,在 Perl 中,使用以下函式:
IV SvIV(SV *sv); /* Perl --> C */
void sv_setiv(SV *sv, IV val); /* C --> Perl */
In Tcl:
在 Tcl 中:
int Tcl_GetLongFromObj(Tcl_Interp *interp, Tcl_Obj *obj, long *value);
Tcl_Obj *Tcl_NewIntObj(long value);
The precise details are not so important. What is important is that all of the underlying type conversion is handled by collections of utility functions and short bits of C code like this--you simply have to read the extension documentation for your favorite language to know how it works (an exercise left to the reader).
確切的細節不是那么重要,重要的是,所有底層型別轉換都由實用程式函式和類似這樣的 C 代碼的短代碼集合處理——你只需閱讀自己喜歡的語言的擴展檔案以了解其作業原理(一個留給讀者的練習),
11.1.2 型別映射
Since type handling is so central to wrapper code generation, SWIG allows it to be completely defined (or redefined) by the user. To do this, a special %typemap directive is used. For example:
由于型別處理對于包裝器代碼生成非常重要,因此 SWIG 允許用戶完全自定義(或重新定義)它,為此,使用特殊的
%typemap指令,例如:
/* Convert from Python --> C */
%typemap(in) int {
$1 = PyInt_AsLong($input);
}
/* Convert from C --> Python */
%typemap(out) int {
$result = PyInt_FromLong($1);
}
At first glance, this code will look a little confusing. However, there is really not much to it. The first typemap (the "in" typemap) is used to convert a value from the target language to C. The second typemap (the "out" typemap) is used to convert in the other direction. The content of each typemap is a small fragment of code that is inserted directly into the SWIG generated wrapper functions. The code is usually C or C++ code which will be generated into the C/C++ wrapper functions. Note that this isn't always the case as some target language modules allow target language code within the typemaps which gets generated into target language specific files. Within this code, a number of special variables prefixed with a $ are expanded. These are really just placeholders for C/C++ variables that are generated in the course of creating the wrapper function. In this case, $input refers to an input object that needs to be converted to C/C++ and $result refers to an object that is going to be returned by a wrapper function. $1 refers to a C/C++ variable that has the same type as specified in the typemap declaration (an int in this example).
A short example might make this a little more clear. If you were wrapping a function like this:
乍看之下,這段代碼看起來有些混亂,但是,實際上并沒有太多,第一個型別映射(
in型別映射)用于將值從目標語言轉換為 C,第二個型別映射(out型別映射)用于向另一個方向轉換,每個型別映射的內容都是一小段代碼,直接插入 SWIG 生成的包裝器函式中,該代碼通常是 C 或 C++ 代碼,它們將生成到 C/C++ 包裝器函式中,請注意,并非總是如此,因為某些目標語言模塊允許型別映射中的目標語言代碼生成到目標語言特定的檔案中,在此代碼中,將擴展許多帶有$前綴的特殊變數,這些實際上只是 C/C++ 變數的占位符,這些變數是在創建包裝器函式的程序中生成的,在這種情況下,$input是指需要轉換為 C/C++ 的輸入物件,而$result是指將由包裝器函式回傳的物件,$1指的是一個 C/C++ 變數,其型別與型別映射宣告中指定的型別相同(本例中為int),一個簡短的示例可能會使這一點更加清楚,如果要包裝這樣的函式:
int gcd(int x, int y);
A wrapper function would look approximately like this:
包裝器函式大致如下所示:
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;
}
In this code, you can see how the typemap code has been inserted into the function. You can also see how the special $ variables have been expanded to match certain variable names inside the wrapper function. This is really the whole idea behind typemaps--they simply let you insert arbitrary code into different parts of the generated wrapper functions. Because arbitrary code can be inserted, it possible to completely change the way in which values are converted.
在此代碼中,你可以看到如何將型別映射代碼插入到函式中,你還可以看到特殊的
$變數是如何擴展的,以匹配包裝器函式中的某些變數名稱,這實際上就是型別映射背后的全部思想,它們只是讓你將任意代碼插入生成的包裝器函式的不同部分,由于可以插入任意代碼,因此可以完全改變值轉換的方式,
11.1.3 模式匹配
As the name implies, the purpose of a typemap is to "map" C datatypes to types in the target language. Once a typemap is defined for a C datatype, it is applied to all future occurrences of that type in the input file. For example:
顧名思義,型別映射的目的是將 C 資料型別“映射”為目標語言中的型別,一旦為 C 資料型別定義型別映射,它將應用于輸入檔案中出現的所有該型別,例如:
/* Convert from Perl --> C */
%typemap(in) int {
$1 = SvIV($input);
}
...
int factorial(int n);
int gcd(int x, int y);
int count(char *s, char *t, int max);
The matching of typemaps to C datatypes is more than a simple textual match. In fact, typemaps are fully built into the underlying type system. Therefore, typemaps are unaffected by typedef, namespaces, and other declarations that might hide the underlying type. For example, you could have code like this:
型別映射與 C 資料型別的匹配不僅僅是簡單的文本匹配,實際上,型別映射完全內置在基礎型別系統中,因此,型別映射不受
typedef、命名空間和其他可能隱藏基礎型別的宣告的影響,例如,你可能具有以下代碼:
/* Convert from Ruby--> C */
%typemap(in) int {
$1 = NUM2INT($input);
}
...
typedef int Integer;
namespace foo {
typedef Integer Number;
};
int foo(int x);
int bar(Integer y);
int spam(foo::Number a, foo::Number b);
In this case, the typemap is still applied to the proper arguments even though typenames don't always match the text "int". This ability to track types is a critical part of SWIG--in fact, all of the target language modules work merely define a set of typemaps for the basic types. Yet, it is never necessary to write new typemaps for typenames introduced by typedef.
In addition to tracking typenames, typemaps may also be specialized to match against a specific argument name. For example, you could write a typemap like this:
在這種情況下,即使型別名并不總是與文本
int匹配,也仍然將型別映射應用于適當的引數,這種跟蹤型別的能力是 SWIG 的重要組成部分——實際上,所有目標語言模塊都只能為基本型別定義一組型別映射,但是,從來沒有必要為typedef引入的型別名撰寫新的型別映射,除了跟蹤型別名稱之外,型別映射還可以專門用于與特定的引數名稱匹配,例如,你可以撰寫這樣的型別映射:
%typemap(in) double nonnegative {
$1 = PyFloat_AsDouble($input);
if ($1 < 0) {
PyErr_SetString(PyExc_ValueError, "argument must be nonnegative.");
SWIG_fail;
}
}
...
double sin(double x);
double cos(double x);
double sqrt(double nonnegative);
typedef double Real;
double log(Real nonnegative);
...
For certain tasks such as input argument conversion, typemaps can be defined for sequences of consecutive arguments. For example:
對于某些任務,例如輸入引數轉換,可以為連續引數序列定義型別映射,例如:
%typemap(in) (char *str, int len) {
$1 = PyString_AsString($input); /* char *str */
$2 = PyString_Size($input); /* int len */
}
...
int count(char *str, int len, char c);
In this case, a single input object is expanded into a pair of C arguments. This example also provides a hint to the unusual variable naming scheme involving $1, $2, and so forth.
在這種情況下,單個輸入物件將擴展為一對 C 引數,這個例子也暗示了涉及不尋常的變數命名方案,包括
$1、$2等等,
11.1.4 復用型別映射
Typemaps are normally defined for specific type and argument name patterns. However, typemaps can also be copied and reused. One way to do this is to use assignment like this:
型別映射通常為特定的型別和引數名稱模式而定義,但是,型別映射也可以復制和重用,一種方法是使用賦值:
%typemap(in) Integer = int;
%typemap(in) (char *buffer, int size) = (char *str, int len);
A more general form of copying is found in the %apply directive like this:
在
%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 };
%apply merely takes all of the typemaps that are defined for one type and applies them to other types. Note: you can include a comma separated set of types in the {...} part of %apply.
It should be noted that it is not necessary to copy typemaps for types that are related by typedef. For example, if you have this,
%apply僅接受為一種型別定義的所有型別映射,并將它們應用于其他型別,注意:你可以在%apply的{...}部分中包含一組用逗號分隔的型別,應該注意的是,沒有必要為
typedef相關的型別復制型別映射,例如,如果你有這個,
typedef int size_t;
then SWIG already knows that the int typemaps apply. You don't have to do anything.
那么 SWIG 已經知道了
int的型別映射,你不必做任何事情,
11.1.5 型別映射能干什么?
The primary use of typemaps is for defining wrapper generation behavior at the level of individual C/C++ datatypes. There are currently six general categories of problems that typemaps address:
型別映射的主要用途是在單一 C/C++ 資料型別級別上定義包裝器生成行為,當前,型別映射解決了六大類問題:
Argument handling
引數處理
int foo(int x, double y, char *s);
- Input argument conversion ("in" typemap).
- Input argument type checking for types used in overloaded methods ("typecheck" typemap).
- Output argument handling ("argout" typemap).
- Input argument value checking ("check" typemap).
- Input argument initialization ("arginit" typemap).
- Default arguments ("default" typemap).
- Input argument resource management ("freearg" typemap).
- 輸入引數轉換(
in型別映射),- 多載方法中的輸入引數型別檢查(
typecheck型別映射),- 輸出引數處理(
argout型別映射),- 輸入引數值檢查(
check型別映射),- 輸入引數初始化(
arginit型別映射),- 默認引數(
default型別映射),- 輸入引數資源管理(
freearg型別映射),
Return value handling
回傳值處理
int foo(int x, double y, char *s);
- Function return value conversion ("out" typemap).
- Return value resource management ("ret" typemap).
- Resource management for newly allocated objects ("newfree" typemap).
- 函式回傳值轉換(
out型別映射),- 回傳殖澩管理(
ret型別映射),- 新分配物件的資源管理(
newfree型別映射),
Exception handling
例外處理
int foo(int x, double y, char *s) throw(MemoryError, IndexError);
- Handling of C++ exception specifications. ("throw" typemap).
- 處理 C++ 例外規范,(
throw型別映射),
Global variables
全域變數
int foo;
- Assignment of a global variable. ("varin" typemap).
- Reading a global variable. ("varout" typemap).
- 分配全域變數,(
varin型別映射),- 讀取全域變數,(
varout型別映射),
Member variables
成員變數
struct Foo {
int x[20];
};
- Assignment of data to a class/structure member. ("memberin" typemap).
- 將資料分配給類或結構體成員,(
memberin型別映射),
Constant creation
創建常量
#define FOO 3
%constant int BAR = 42;
enum { ALE, LAGER, STOUT };
- Creation of constant values. ("consttab" or "constcode" typemap).
Details of each of these typemaps will be covered shortly. Also, certain language modules may define additional typemaps that expand upon this list. For example, the Java module defines a variety of typemaps for controlling additional aspects of the Java bindings. Consult language specific documentation for further details.
- 創建常數值,(
consttab或constcode型別映射),每個型別映射的詳細內容很快會提到,同樣,某些語言模塊可能會定義其他型別映射以擴展此串列,例如,Java 模塊定義了各種型別映射來控制 Java 系結的其他方面,請查閱特定于語言的檔案以獲取更多詳細資訊,
11.1.6 型別映射不能干什么?
Typemaps can't be used to define properties that apply to C/C++ declarations as a whole. For example, suppose you had a declaration like this,
型別映射不能用于定義整體上適用于 C/C++ 宣告的屬性,例如,假設你有一個這樣的宣告,
Foo *make_Foo(int n);
and you wanted to tell SWIG that make_Foo(int n) returned a newly allocated object (for the purposes of providing better memory management). Clearly, this property of make_Foo(int n) is not a property that would be associated with the datatype Foo * by itself. Therefore, a completely different SWIG customization mechanism (%feature) is used for this purpose. Consult the Customization Features chapter for more information about that.
Typemaps also can't be used to rearrange or transform the order of arguments. For example, if you had a function like this:
并且你想告訴 SWIG
make_Foo(int n)回傳了一個新分配的物件(目的是提供更好的記憶體管理),顯然,make_Foo(int n)的此屬性不是本身將與資料型別Foo *相關聯的屬性,因此,為此目的要使用完全不同的 SWIG 定制機制(%feature),有關更多資訊,請參考自定義功能章節,型別映射也不能用于重新排列或轉換引數的順序,例如,如果你具有如下函式:
void foo(int, char *);
you can't use typemaps to interchange the arguments, allowing you to call the function like this:
你不能使用型別映射來交換引數,進而允許你能這樣呼叫函式:
foo("hello", 3) # Reversed arguments
If you want to change the calling conventions of a function, write a helper function instead. For example:
如果要更改函式的呼叫約定,請撰寫輔助函式,例如:
%rename(foo) wrap_foo;
%inline %{
void wrap_foo(char *s, int x) {
foo(x, s);
}
%}
11.1.7 與面向切面編程的相似之處
SWIG has parallels to Aspect Oriented Software Development (AOP). The AOP terminology with respect to SWIG typemaps can be viewed as follows:
- Cross-cutting concerns: The cross-cutting concerns are the modularization of the functionality that the typemaps implement, which is primarily marshalling of types from/to the target language and C/C++.
- Advice: The typemap body contains code which is executed whenever the marshalling is required.
- Pointcut: The pointcuts are the positions in the wrapper code that the typemap code is generated into.
- Aspect: Aspects are the combination of the pointcut and the advice, hence each typemap is an aspect.
SWIG can also be viewed as has having a second set of aspects based around %feature. Features such as %exception are also cross-cutting concerns as they encapsulate code that can be used to add logging or exception handling to any function.
SWIG 與面向切面的軟體開發(AOP)相似,與 SWIG 型別映射有關的 AOP 術語如下:
- 橫切關注點:橫切關注點是型別映射所實作功能的模塊化,主要是將目標語言和 C/C++ 之間的型別進行編組,
- 通知:型別映射主體包含在需要編組時執行的代碼,
- 切入點:切入點是包裝器代碼中生成型別映射代碼的位置,
- 切面:切面是切入點和通知的組合,因此每個型別映射都是一個切面,
也可以將 SWIG 視為具有基于
%feature的第二組切面,諸如%exception之類的功能也是橫切關注點,因為它們封裝了可用于向任何函式添加日志記錄或例外處理的代碼,
11.1.8 本章的剩余部分
The rest of this chapter provides detailed information for people who want to write new typemaps. This information is of particular importance to anyone who intends to write a new SWIG target language module. Power users can also use this information to write application specific type conversion rules.
Since typemaps are strongly tied to the underlying C++ type system, subsequent sections assume that you are reasonably familiar with the basic details of values, pointers, references, arrays, type qualifiers (e.g., const), structures, namespaces, templates, and memory management in C/C++. If not, you would be well-advised to consult a copy of "The C Programming Language" by Kernighan and Ritchie or "The C++ Programming Language" by Stroustrup before going any further.
本章的剩余部分為想要撰寫新的型別映射的人提供了詳細的資訊,對于打算為 SWIG 撰寫新目標語言模塊的人來說,這些資訊都特別重要,高級用戶還可以使用這些資訊來撰寫應用程式特定的型別轉換規則,
由于型別映射與底層 C++ 型別系統緊密相關,因此后續章節假定你對值、指標、參考、陣列、型別限定符(例如
const)、結構體、命名空間、模板和 C/C++ 中的記憶體管理相當熟悉,如果不是這樣,建議你先閱讀 Kernighan 和 Ritchie 撰寫的《The C Programming Language》或 Stroustrup 撰寫的《The C++ Programming Language》,
11.2 型別映射詳述
This section describes the behavior of the %typemap directive itself.
本節描述了
%typemap指令本身的行為,
11.2.1 定義一個型別映射
New typemaps are defined using the %typemap declaration. The general form of this declaration is as follows (parts enclosed in [...] are optional):
新的型別映射使用
%typemap宣告定義,該宣告的一般形式如下([...]中的部分是可選的):
%typemap(method [, modifiers]) typelist code ;
method is a simply a name that specifies what kind of typemap is being defined. It is usually a name like "in", "out", or "argout". The purpose of these methods is described later.
modifiers is an optional comma separated list of name="value" values. These are sometimes to attach extra information to a typemap and is often target-language dependent. They are also known as typemap attributes.
typelist is a list of the C++ type patterns that the typemap will match. The general form of this list is as follows:
method是一個簡單的名稱,用于指定要定義的型別映射,通常,它的名稱類似于in、out或argout,這些方法的目的將在后面說明,
modifiers是一個可選的逗號分隔串列,其中包含name="value"值,這些有時會在型別映射上附加額外的資訊,并且通常取決于目標語言,它們也稱為型別映射屬性,
typelist是型別映射將匹配的 C++ 型別模式的串列,此串列的一般形式如下:
typelist : typepattern [, typepattern, typepattern, ... ] ;
typepattern : type [ (parms) ]
| type name [ (parms) ]
| ( typelist ) [ (parms) ]
Each type pattern is either a simple type, a simple type and argument name, or a list of types in the case of multi-argument typemaps. In addition, each type pattern can be parameterized with a list of temporary variables (parms). The purpose of these variables will be explained shortly.
code specifies the code used in the typemap. Usually this is C/C++ code, but in the statically typed target languages, such as Java and C#, this can contain target language code for certain typemaps. It can take any one of the following forms:
每個型別模式可以是簡單型別、簡單型別和引數名稱,或者在多引數型別映射下的型別串列,此外,可以使用一系列臨時變數(引數)對每個型別模式進行引數化,這些變數的目的將在稍后說明,
code指定型別映射中使用的代碼,通常這是 C/C++ 代碼,但是在靜態型別的目標語言(例如 Java 和 C#)中,它可以包含某些型別映射的目標語言代碼,可以采用以下任何一種形式:
code : { ... }
| " ... "
| %{ ... %}
Note that the preprocessor will expand code within the {} delimiters, but not in the last two styles of delimiters, see Preprocessor and Typemaps. Here are some examples of valid typemap specifications:
請注意,前處理器將在
{}分隔符內擴展代碼,但不會在最后兩種分隔符樣式中擴展代碼,請參閱《前處理器與型別映射》章節,以下是有效型別映射規范的一些示例:
/* Simple typemap declarations */
%typemap(in) int {
$1 = PyInt_AsLong($input);
}
%typemap(in) int "$1 = PyInt_AsLong($input);";
%typemap(in) int %{
$1 = PyInt_AsLong($input);
%}
/* Typemap with extra argument name */
%typemap(in) int nonnegative {
...
}
/* Multiple types in one typemap */
%typemap(in) int, short, long {
$1 = SvIV($input);
}
/* Typemap with modifiers */
%typemap(in, doc="integer") int "$1 = scm_to_int($input);";
/* Typemap applied to patterns of multiple arguments */
%typemap(in) (char *str, int len),
(char *buffer, int size)
{
$1 = PyString_AsString($input);
$2 = PyString_Size($input);
}
/* Typemap with extra pattern parameters */
%typemap(in, numinputs=0) int *output (int temp),
long *output (long temp)
{
$1 = &temp;
}
Admittedly, it's not the most readable syntax at first glance. However, the purpose of the individual pieces will become clear.
乍一看,這并不是最易讀的語法,但是,各個部分的目的將變得清楚,
11.2.2 型別映射作用范圍
Once defined, a typemap remains in effect for all of the declarations that follow. A typemap may be redefined for different sections of an input file. For example:
定義后,型別映射對于隨后出現的所有宣告都有效,可以為輸入檔案的不同部分重新定義型別映射,例如:
// typemap1
%typemap(in) int {
...
}
int fact(int); // typemap1
int gcd(int x, int y); // typemap1
// typemap2
%typemap(in) int {
...
}
int isprime(int); // typemap2
One exception to the typemap scoping rules pertains to the %extend declaration. %extend is used to attach new declarations to a class or structure definition. Because of this, all of the declarations in an %extend block are subject to the typemap rules that are in effect at the point where the class itself is defined. For example:
型別映射范圍規則的一個例外與
%extend宣告有關,%extend用于將新的宣告附加到類或結構體定義上,因此,%extend塊中的所有宣告都將受到型別映射規則的約束,該規則在定義類本身時生效,例如:
class Foo {
...
};
%typemap(in) int {
...
}
%extend Foo {
int blah(int x); // typemap has no effect. Declaration is attached to Foo which
// appears before the %typemap declaration.
};
11.2.3 復制型別映射
A typemap is copied by using assignment. For example:
使用賦值復制型別映射,例如:
%typemap(in) Integer = int;
or this:
或者這樣:
%typemap(in) Integer, Number, int32_t = int;
Types are often managed by a collection of different typemaps. For example:
型別通常由不同型別映射的集合來管理,例如:
%typemap(in) int { ... }
%typemap(out) int { ... }
%typemap(varin) int { ... }
%typemap(varout) int { ... }
To copy all of these typemaps to a new type, use %apply. For example:
要將所有這些型別映射復制到一個新的型別,請使用
%apply,例如:
%apply int { Integer }; // Copy all int typemaps to Integer
%apply int { Integer, Number }; // Copy all int typemaps to both Integer and Number
The patterns for %apply follow the same rules as for %typemap. For example:
%apply的模式遵循與%typemap相同的規則,例如:
%apply int *output { Integer *output }; // Typemap with name
%apply (char *buf, int len) { (char *buffer, int size) }; // Multiple arguments
11.2.4 洗掉型別映射
A typemap can be deleted by simply defining no code. For example:
不需要定義代碼即可洗掉型別映射,例如:
%typemap(in) int; // Clears typemap for int
%typemap(in) int, long, short; // Clears typemap for int, long, short
%typemap(in) int *output;
The %clear directive clears all typemaps for a given type. For example:
%clear指令清除給定型別的所有型別映射,例如:
%clear int; // Removes all types for int
%clear int *output, long *output;
Note: Since SWIG's default behavior is defined by typemaps, clearing a fundamental type like int will make that type unusable unless you also define a new set of typemaps immediately after the clear operation.
注意:由于 SWIG 的默認行為是由型別映射定義的,因此除非清除操作之后立即定義了一組新的型別映射,否則清除基本型別(如
int)將使該型別不可用,
11.2.5 型別映射的位置
Typemap declarations can be declared in the global scope, within a C++ namespace, and within a C++ class. For example:
可以在全域范圍、C++ 命名空間和 C++ 類中宣告型別映射,例如:
%typemap(in) int {
...
}
namespace std {
class string;
%typemap(in) string {
...
}
}
class Bar {
public:
typedef const int & const_reference;
%typemap(out) const_reference {
...
}
};
When a typemap appears inside a namespace or class, it stays in effect until the end of the SWIG input (just like before). However, the typemap takes the local scope into account. Therefore, this code
當型別映射出現在命名空間或類中時,它直到 SWIG 輸入檔案的結束(就像之前一樣)一直有效,但是,型別映射將區域范圍考慮在內,因此,此代碼
namespace std {
class string;
%typemap(in) string {
...
}
}
is really defining a typemap for the type std::string. You could have code like this:
確實為
std::string型別定義了一個型別映射,你可能會有這樣的代碼:
namespace std {
class string;
%typemap(in) string { /* std::string */
...
}
}
namespace Foo {
class string;
%typemap(in) string { /* Foo::string */
...
}
}
In this case, there are two completely distinct typemaps that apply to two completely different types (std::string and Foo::string).
It should be noted that for scoping to work, SWIG has to know that string is a typename defined within a particular namespace. In this example, this is done using the forward class declaration class string.
在這種情況下,有兩個完全不同的型別映射適用于兩個完全不同的型別(
std::string和Foo::string),應當注意,為使作用域有效,SWIG 必須知道
string是在特定名稱空間內定義的型別名,在此示例中,這是使用正向類宣告class string完成的,
11.3 模式匹配規則
The section describes the pattern matching rules by which C/C++ datatypes are associated with typemaps. The matching rules can be observed in practice by using the debugging options also described.
本節描述了模式匹配規則,通過這些規則,C/C++ 資料型別與型別映射相關聯,實際中,可以通過使用除錯選項來觀察匹配規則,
11.3.1 基本匹配規則
Typemaps are matched using both a type and a name (typically the name of a argument). For a given TYPE NAME pair, the following rules are applied, in order, to find a match. The first typemap found is used.
- Typemaps that exactly match
TYPEandNAME. - Typemaps that exactly match
TYPEonly. - If
TYPEis a C++ template of typeT<TPARMS>, whereTPARMSare the template parameters, the type is stripped of the template parameters and the following checks are then made:- Typemaps that exactly match
TandNAME. - Typemaps that exactly match
Tonly.
- Typemaps that exactly match
If TYPE includes qualifiers (const, volatile, etc.), each qualifier is stripped one at a time to form a new stripped type and the matching rules above are repeated on the stripped type. The left-most qualifier is stripped first, resulting in the right-most (or top-level) qualifier being stripped last. For example int const*const is first stripped to int *const then int *.
If TYPE is an array. The following transformation is made:
- Replace all dimensions to
[ANY]and look for a generic array typemap.
To illustrate, suppose that you had a function like this:
使用型別和名稱(通常是引數名稱)來匹配型別映射,對于給定的
TYPE NAME配對,將應用以下規則來查找匹配項,第一個找到的型別映射將被使用,
- 與
TYPE和NAME完全匹配的型別映射,- 僅與
TYPE完全匹配的型別映射,- 如果
TYPE是T<TPARMS>型別的 C++ 模板,其中TPARMS是模板引數,則將型別的模板引數剝離,然后進行以下檢查:
- 與
T和NAME完全匹配的型別映射,- 僅與
T完全匹配的型別映射,如果
TYPE包含限定符(const、volatile等),則每次剝離一個限定符以形成新的剝離型別,并在剝離型別上重復上述匹配規則,最左邊的限定符首先被剝離,最右邊的(或頂級)限定符最后被剝離,例如,首先將int const * const剝離為int * const,然后剝離為int *,如果
TYPE是一個陣列,進行以下轉換:
- 將所有維度替換為
[ANY],并查找通用陣列型別映射為了說明這一點,假設你具有如下函式:
int foo(const char *s);
To find a typemap for the argument const char *s, SWIG will search for the following typemaps:
要為引數
const char *s查找型別映射,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)
When more than one typemap rule might be defined, only the first match found is actually used. Here is an example that shows how some of the basic rules are applied:
當可能定義多個型別映射規則時,實際上僅使用找到的第一個匹配項,下面是一個示例,顯示了如何應用一些基本規則:
%typemap(in) int *x {
... typemap 1
}
%typemap(in) int * {
... typemap 2
}
%typemap(in) const int *z {
... typemap 3
}
%typemap(in) int [4] {
... typemap 4
}
%typemap(in) int [ANY] {
... typemap 5
}
void A(int *x); // int *x rule (typemap 1)
void B(int *y); // int * rule (typemap 2)
void C(const int *x); // int *x rule (typemap 1)
void D(const int *z); // const int *z rule (typemap 3)
void E(int x[4]); // int [4] rule (typemap 4)
void F(int x[1000]); // int [ANY] rule (typemap 5)
Compatibility note: SWIG-2.0.0 introduced stripping the qualifiers one step at a time. Prior versions stripped all qualifiers in one step.
注意兼容性:SWIG-2.0.0 引入了一次洗掉一個限定符,先前的版本一次就消除了所有限定符,
11.3.2 typedef 還原匹配
If no match is found using the rules in the previous section, SWIG applies a typedef reduction to the type and repeats the typemap search for the reduced type. To illustrate, suppose you had code like this:
如果使用上一節中的規則未找到匹配項,則 SWIG 將
typedef還原,然后對還原后的型別重復進行型別映射搜索,為了說明這一點,假設你有如下代碼:
%typemap(in) int {
... typemap 1
}
typedef int Integer;
void blah(Integer x);
To find the typemap for Integer x, SWIG will first search for the following typemaps:
為了找到
Integer x的型別映射,SWIG 將首先搜索以下型別映射:
Integer x
Integer
Finding no match, it then applies a reduction Integer -> int to the type and repeats the search.
如果找不到匹配項,則對型別應用還原
Integer -> int,并重復搜索,
int x
int --> match: typemap 1
Even though two types might be the same via typedef, SWIG allows typemaps to be defined for each typename independently. This allows for interesting customization possibilities based solely on the typename itself. For example, you could write code like this:
即使兩個型別通過
typedef可能是相同的,SWIG 仍允許為每個型別名分別定義型別映射,這允許僅基于型別名稱本身進行有趣的自定義,例如,你可以撰寫如下代碼:
typedef double pdouble; // Positive double
// typemap 1
%typemap(in) double {
... get a double ...
}
// typemap 2
%typemap(in) pdouble {
... get a positive double ...
}
double sin(double x); // typemap 1
pdouble sqrt(pdouble x); // typemap 2
When reducing the type, only one typedef reduction is applied at a time. The search process continues to apply reductions until a match is found or until no more reductions can be made.
For complicated types, the reduction process can generate a long list of patterns. Consider the following:
還原型別時,一次僅還原一次
typedef,搜索程序將繼續應用還原直到找到匹配項,或無法再進行還原,對于復雜型別,還原程序可以生成一長串模式,考慮以下:
typedef int Integer;
typedef Integer Row4[4];
void foo(Row4 rows[10]);
To find a match for the Row4 rows[10] argument, SWIG would check the following patterns, stopping only when it found a match:
為了找到
Row4 rows[10]引數的匹配項,SWIG 將檢查以下模式,僅在找到匹配項時停止:
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]
For parameterized types like templates, the situation is even more complicated. Suppose you had some declarations like this:
對于像模板這樣的引數化型別,情況甚至更加復雜,假設你有一些這樣的宣告:
typedef int Integer;
typedef foo<Integer, Integer> fooii;
void blah(fooii *x);
In this case, the following typemap patterns are searched for the argument fooii *x:
在這種情況下,將在以下型別映射模式中搜索引數
fooii *x:
fooii *x
fooii *
# Reduce fooii --> foo<Integer, Integer>
foo<Integer, Integer> *x
foo<Integer, Integer> *
# Reduce Integer -> int
foo<int, Integer> *x
foo<int, Integer> *
# Reduce Integer -> int
foo<int, int> *x
foo<int, int> *
Typemap reductions are always applied to the left-most type that appears. Only when no reductions can be made to the left-most type are reductions made to other parts of the type. This behavior means that you could define a typemap for foo<int, Integer>, but a typemap for foo<Integer, int> would never be matched. Admittedly, this is rather esoteric--there's little practical reason to write a typemap quite like that. Of course, you could rely on this to confuse your coworkers even more.
As a point of clarification, it is worth emphasizing that typedef matching is a typedef reduction process only, that is, SWIG does not search for every single possible typedef. Given a type in a declaration, it will only reduce the type, it won't build it up looking for typedefs. For example, given the type Struct, the typemap below will not be used for the aStruct parameter, because Struct is fully reduced:
還原型別映射始終應用于出現在最左側的型別,僅當無法對最左邊的型別進行還原時,才對型別的其他部分進行還原,這種行為意味著你可以為
foo<int, Integer>定義一個型別映射,但是foo<Integer, int>的型別映射不會被匹配,誠然,這是相當不常見的——幾乎沒有實際的理由來撰寫類似的型別映射,當然,你可以用它使你的同事更加困惑,需要澄清一點,值得強調的是
typedef匹配僅是typedef的“還原”程序,也就是說,SWIG 不會搜索每個可能的typedef,給定宣告中的型別,它只會還原型別,而不會在尋找typedef時建立它,例如,給定型別為Struct,由于Struct已被完全還原,因此以下型別映射將不會用于aStruct引數:
struct Struct {...};
typedef Struct StructTypedef;
%typemap(in) StructTypedef {
...
}
void go(Struct aStruct);
11.3.3 默認型別映射匹配規則
If the basic pattern matching rules result in no match being made, even after typedef reductions, the default typemap matching rules are used to look for a suitable typemap match. These rules match a generic typemap based on the reserved SWIGTYPE base type. For example pointers will use SWIGTYPE *and references will use SWIGTYPE &. More precisely, the rules are based on the C++ class template partial specialization matching rules used by C++ compilers when looking for an appropriate partial template specialization. This means that a match is chosen from the most specialized set of generic typemap types available. For example, when looking for a match to int const *, the rules will prefer to match SWIGTYPE const * if available before matching SWIGTYPE *, before matching SWIGTYPE.
Most SWIG language modules use typemaps to define the default behavior of the C primitive types. This is entirely straightforward. For example, a set of typemaps for primitives marshalled by value or const reference are written like this:
如果即使在還原
typedef之后基本模式匹配規則最終沒有匹配,將使用默認的型別映射匹配規則來尋找合適的匹配,這些規則匹配基于保留的SWIGTYPE基本型別的通用型別映射,例如,指標將使用SWIGTYPE *,而參考將使用SWIGTYPE &,更準確地說,這些規則基于 C++ 類模板偏特化匹配規則,這些匹配規則由 C++ 編譯器在尋找合適的模板偏特化時使用,這意味著從可用的最特定的通用型別映射型別集合中選擇一個匹配項,例如,當尋找與int const *的匹配項時,規則將優先匹配SWIGTYPE const *(如果有的話),然后再匹配SWIGTYPE *,再匹配SWIGTYPE,大多數 SWIG 語言模塊都使用型別映射來定義 C 基本型別的默認行為,這是非常簡單的,例如,按值或常參考編組的原始型別的一組型別映射如下所示:
%typemap(in) int "... convert to int ...";
%typemap(in) short "... convert to short ...";
%typemap(in) float "... convert to float ...";
...
%typemap(in) const int & "... convert ...";
%typemap(in) const short & "... convert ...";
%typemap(in) const float & "... convert ...";
...
Since typemap matching follows all typedef declarations, any sort of type that is mapped to a primitive type by value or const reference through typedef will be picked up by one of these primitive typemaps. Most language modules also define typemaps for char pointers and char arrays to handle strings, so these non-default types will also be used in preference as the basic typemap matching rules provide a better match than the default typemap matching rules.
Below is a list of the typical default types supplied by language modules, showing what the "in" typemap would look like:
由于型別映射匹配遵循所有的
typedef宣告,因此通過typedef通過值或常參考映射到原始型別的任何型別的型別都將被這些原始型別映射之一所拾取,大多數語言模塊還為char指標和char陣列定義了型別映射以處理字串,因此這些非默認型別也將優先使用,因為基本的型別映射匹配規則比默認的型別映射匹配規則提供了更好的匹配,下面是語言模塊提供的典型默認型別的串列,顯示了
in型別映射的樣子:
%typemap(in) SWIGTYPE & { ... default reference handling ... };
%typemap(in) SWIGTYPE * { ... default pointer handling ... };
%typemap(in) SWIGTYPE *const { ... default pointer const handling ... };
%typemap(in) SWIGTYPE *const& { ... default pointer const reference handling ... };
%typemap(in) SWIGTYPE[ANY] { ... 1D fixed size arrays handlling ... };
%typemap(in) SWIGTYPE [] { ... unknown sized array handling ... };
%typemap(in) enum SWIGTYPE { ... default handling for enum values ... };
%typemap(in) const enum SWIGTYPE & { ... default handling for const enum reference values ... };
%typemap(in) SWIGTYPE (CLASS::*) { ... default pointer member handling ... };
%typemap(in) SWIGTYPE { ... simple default handling ... };
If you wanted to change SWIG's default handling for simple pointers, you would simply redefine the rule for SWIGTYPE *. Note, the simple default typemap rule is used to match against simple types that don't match any other rules:
如果你想更改 SWIG 對簡單指標的默認處理,很簡單,只需為
SWIGTYPE *重新定義規則,請注意,簡單的默認型別映射規則用于與不匹配任何其他規則的簡單型別進行匹配:
%typemap(in) SWIGTYPE { ... simple default handling ... }
This typemap is important because it is the rule that gets triggered when call or return by value is used. For instance, if you have a declaration like this:
此型別映射很重要,因為使用呼叫或按值回傳時會觸發該規則,例如,如果你有這樣的宣告:
double dot_product(Vector a, Vector b);
The Vector type will usually just get matched against SWIGTYPE. The default implementation of SWIGTYPEis to convert the value into pointers (as described in this earlier section).
By redefining SWIGTYPE it may be possible to implement other behavior. For example, if you cleared all typemaps for SWIGTYPE, SWIG simply won't wrap any unknown datatype (which might be useful for debugging). Alternatively, you might modify SWIGTYPE to marshal objects into strings instead of converting them to pointers.
Let's consider an example where the following typemaps are defined and SWIG is looking for the best match for the enum shown below:
Vector型別通常只會與SWIGTYPE相匹配,SWIGTYPE的默認實作是將值轉換為指標(如本之前的章節所述),通過重新定義
SWIGTYPE,可以實作其他行為,例如,如果你清除了SWIGTYPE的所有型別映射,則 SWIG 不會包裝任何未知的資料型別(這可能對除錯很有用),或者,你可以修改SWIGTYPE以將物件編組為字串,而不是將它們轉換為指標,讓我們考慮一個示例,其中定義了以下型別映射,并且 SWIG 正在為以下所示的列舉尋找最佳匹配:
%typemap(in) const Hello & { ... }
%typemap(in) const enum SWIGTYPE & { ... }
%typemap(in) enum SWIGTYPE & { ... }
%typemap(in) SWIGTYPE & { ... }
%typemap(in) SWIGTYPE { ... }
enum Hello {};
const Hello &hi;
The typemap at the top of the list will be chosen, not because it is defined first, but because it is the closest match for the type being wrapped. If any of the typemaps in the above list were not defined, then the next one on the list would have precedence.
The best way to explore the default typemaps is to look at the ones already defined for a particular language module. Typemap definitions are usually found in the SWIG library in a file such as java.swg, csharp.swg etc. However, for many of the target languages the typemaps are hidden behind complicated macros, so the best way to view the default typemaps, or any typemaps for that matter, is to look at the preprocessed output by running swig -E on any interface file. Finally the best way to view the typemap matching rules in action is via the debugging typemap pattern matching options covered later on.
Compatibility note: The default typemap matching rules were modified in SWIG-2.0.0 from a slightly simpler scheme to match the current C++ class template partial specialization matching rules.
將選擇串列頂部的型別映射,不僅是因為首先定義了它,而且是因為它與被包裝的型別最匹配,如果上面串列中的任何型別映射未定義,則串列中的下一個優先,
探索默認型別映射的最佳方法是查看已為特定語言模塊定義的映射,型別映射定義通常可以在 SWIG 庫的
java.swg、csharp.swg等檔案中找到,但是,對于許多目標語言而言,型別映射都隱藏在復雜的宏后面,因此,查看默認型別映射或任何與此相關的型別映射的最佳方法是在任何介面檔案上運行swig -E來查看預處理后的輸出,最后,查看正在使用的型別映射匹配規則的最佳方法是通過稍后介紹的除錯型別映射匹配模式選項,注意兼容性:默認的型別映射匹配規則是在 SWIG-2.0.0 中從稍微簡單的方案中修改的,以匹配當前的 C++ 類模板偏特化匹配規則,
11.3.4 多引數型別映射
When multi-argument typemaps are specified, they take precedence over any typemaps specified for a single type. For example:
指定多引數型別映射時,它們優先于為單個型別指定的任何型別映射,例如:
%typemap(in) (char *buffer, int len) {
// typemap 1
}
%typemap(in) char *buffer {
// typemap 2
}
void foo(char *buffer, int len, int count); // (char *buffer, int len)
void bar(char *buffer, int blah); // char *buffer
Multi-argument typemaps are also more restrictive in the way that they are matched. Currently, the first argument follows the matching rules described in the previous section, but all subsequent arguments must match exactly.
多引數型別映射在匹配方式上也有更多限制,當前,第一個引數遵循上一節中描述的匹配規則,但是所有后續引數必須完全匹配,
11.3.5 匹配規則對比 C++ 模板
For those intimately familiar with C++ templates, a comparison of the typemap matching rules and template type deduction is interesting. The two areas considered are firstly the default typemaps and their similarities to partial template specialization and secondly, non-default typemaps and their similarities to full template specialization.
For default (SWIGTYPE) typemaps the rules are inspired by C++ class template partial specialization. For example, given partial specialization for T const& :
對于那些熟悉 C++ 模板的人來說,比較型別映射匹配規則和模板型別推導是很有趣的,首先考慮的兩個方面是默認型別映射及其與模板偏特化的相似性,其次是非默認型別映射及其與模板完全化的相似性,
對于默認(
SWIGTYPE)型別映射,規則受 C++ 類模板偏特化的啟發,例如,給定T const&的偏特化:
template <typename T> struct X { void a(); };
template <typename T> struct X< T const& > { void b(); };
The full (unspecialized) template is matched with most types, such as:
完全(非偏)模板與大多數型別匹配,例如:
X< int & > x1; x1.a();
and the following all match the T const& partial specialization:
以及以下所有匹配
T const&的偏特化的代碼:
X< int *const& > x2; x2.b();
X< int const*const& > x3; x3.b();
X< int const& > x4; x4.b();
Now, given just these two default typemaps, where T is analogous to SWIGTYPE:
現在,僅給出這兩個默認型別映射,其中
T類似于SWIGTYPE:
%typemap(...) SWIGTYPE { ... }
%typemap(...) SWIGTYPE const& { ... }
The generic default typemap SWIGTYPE is used with most types, such as
通用默認型別映射
SWIGTYPE用于大多數型別,例如
int &
and the following all match the SWIGTYPE const& typemap, just like the partial template matching:
并且以下所有內容都匹配
SWIGTYPE const&型別映射,就像部分模板匹配一樣:
int *const&
int const*const&
int const&
Note that the template and typemap matching rules are not identical for all default typemaps though, for example, with arrays.
For non-default typemaps, one might expect SWIG to follow the fully specialized template rules. This is nearly the case, but not quite. Consider a very similar example to the earlier partially specialized template but this time there is a fully specialized template:
請注意,模板和型別映射匹配規則對于所有默認型別映射都不相同,例如,對于陣列,
對于非默認型別映射,可能希望 SWIG 遵循完全特化的模板規則,這幾乎是事實,但事實并非如此,考慮一個與早期的偏特化模板非常相似的示例,但是這次有一個完全特化的模板:
template <typename T> struct Y { void a(); };
template <> struct Y< int const & > { void b(); };
Only the one type matches the specialized template exactly:
只有一種型別與特化模板完全匹配:
Y< int & > y1; y1.a();
Y< int *const& > y2; y2.a();
Y< int const *const& > y3; y3.a();
Y< int const& > y4; y4.b(); // fully specialized match
Given typemaps with the same types used for the template declared above, where T is again analogous to SWIGTYPE:
給定具有與上面宣告的模板相同型別的型別映射,其中
T再次類似于SWIGTYPE:
%typemap(...) SWIGTYPE { ... }
%typemap(...) int const& { ... }
The comparison between non-default typemaps and fully specialized single parameter templates turns out to be the same, as just the one type will match the non-default typemap:
事實證明,非默認型別映射和完全特化的單引數模板之間的比較是相同的,因為只有一種型別會匹配非默認型別映射:
int &
int *const&
int const*const&
int const& // matches non-default typemap int const&
However, if a non-const type is used instead:
但是,如果改用非常量型別:
%typemap(...) SWIGTYPE { ... }
%typemap(...) int & { ... }
then there is a clear difference to template matching as both the const and non-const types match the typemap:
那么模板匹配有明顯的區別,因為
const和非const型別都與型別映射匹配:
int & // matches non-default typemap int &
int *const&
int const*const&
int const& // matches non-default typemap int &
There are other subtle differences such as typedef handling, but at least it should be clear that the typemap matching rules are similar to those for specialized template handling.
還有其他一些細微的差異,例如
typedef處理,但至少應該清楚的是,型別映射匹配規則類似于特化模板處理的規則,
11.3.6 除錯型別映射模式匹配
There are two useful debug command line options available for debugging typemaps, -debug-tmsearchand -debug-tmused.
The -debug-tmsearch option is a verbose option for debugging typemap searches. This can be very useful for watching the pattern matching process in action and for debugging which typemaps are used. The option displays all the typemaps and types that are looked for until a successful pattern match is made. As the display includes searches for each and every type needed for wrapping, the amount of information displayed can be large. Normally you would manually search through the displayed information for the particular type that you are interested in.
For example, consider some of the code used in the Typedef reductions section already covered:
有兩個有用的除錯命令列選項可用于除錯型別映射:
-debug-tmsearch和-debug-tmused,
-debug-tmsearch選項是用于除錯型別映射搜索的詳細選項,這對于觀察實際的模式匹配程序,以及除錯使用哪種型別映射非常有用,該選項顯示在成功進行模式匹配之前要查找的所有型別映射和型別,由于顯示內容包括對包裝所需的每種型別的搜索,因此顯示的資訊量可能很大,通常,你將在顯示的資訊中手動搜索感興趣的特定型別,例如,考慮已經討論過的還原
typedef章節中使用的一些代碼:
typedef int Integer;
typedef Integer Row4[4];
void foo(Row4 rows[10]);
A sample of the debugging output is shown below for the "in" typemap:
下面顯示了
in型別映射的除錯輸出示例:
swig -perl -debug-tmsearch example.i
...
example.h:3: Searching for a suitable 'in' typemap for: Row4 rows[10]
Looking for: Row4 rows[10]
Looking for: Row4 [10]
Looking for: Row4 rows[ANY]
Looking for: Row4 [ANY]
Looking for: Integer rows[10][4]
Looking for: Integer [10][4]
Looking for: Integer rows[ANY][ANY]
Looking for: Integer [ANY][ANY]
Looking for: int rows[10][4]
Looking for: int [10][4]
Looking for: int rows[ANY][ANY]
Looking for: int [ANY][ANY]
Looking for: SWIGTYPE rows[ANY][ANY]
Looking for: SWIGTYPE [ANY][ANY]
Looking for: SWIGTYPE rows[ANY][]
Looking for: SWIGTYPE [ANY][]
Looking for: SWIGTYPE *rows[ANY]
Looking for: SWIGTYPE *[ANY]
Looking for: SWIGTYPE rows[ANY]
Looking for: SWIGTYPE [ANY]
Looking for: SWIGTYPE rows[]
Looking for: SWIGTYPE []
Using: %typemap(in) SWIGTYPE []
...
showing that the best default match supplied by SWIG is the SWIGTYPE [] typemap. As the example shows, the successful match displays the used typemap source including typemap method, type and optional name in one of these simplified formats:
Using: %typemap(method) type nameUsing: %typemap(method) type name = type2 name2Using: %apply type2 name2 { type name }
This information might meet your debugging needs, however, you might want to analyze further. If you next invoke SWIG with the -E option to display the preprocessed output, and search for the particular typemap used, you'll find the full typemap contents (example shown below for Python):
表明 SWIG 提供的最佳默認匹配項是
SWIGTYPE []型別映射,如示例所示,成功匹配以下列簡化格式之一顯示使用的型別映射,包括型別映射方法、型別和可選名稱:
Using: %typemap(method) type nameUsing: %typemap(method) type name = type2 name2Using: %apply type2 name2 { type name }此資訊可能滿足你的除錯需求,但是,你可能需要進一步分析,如果接下來使用
-E選項呼叫 SWIG 以顯示預處理后的輸出,并搜索所使用的特定型別映射,則將找到完整的型別映射內容(以下示例顯示在 Python 中):
%typemap(in, noblock=1) SWIGTYPE [] (void *argp = 0, int res = 0) {
res = SWIG_ConvertPtr($input, &argp, $descriptor, $disown | 0 );
if (!SWIG_IsOK(res)) {
SWIG_exception_fail(SWIG_ArgError(res), "in method '" "$symname" "', argument "
"$argnum"" of type '" "$type""'");
}
$1 = ($ltype)(argp);
}
The generated code for the foo wrapper will then contain the snippets of the typemap with the special variables expanded. The rest of this chapter will need reading though to fully understand all of this, however, the relevant parts of the generated code for the above typemap can be seen below:
然后,為
foo包裝程式生成的代碼將包含帶有特殊變數擴展的型別映射的代碼段,本章的其余部分雖然需要閱讀才能完全理解所有這些內容,但是,可以在下面看到上述型別映射的生成代碼的相關部分:
SWIGINTERN PyObject *_wrap_foo(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
...
void *argp1 = 0 ;
int res1 = 0 ;
...
res1 = SWIG_ConvertPtr(obj0, &argp1, SWIGTYPE_p_a_4__int, 0 | 0 );
if (!SWIG_IsOK(res1)) {
SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "foo" "', argument "
"1"" of type '" "int [10][4]""'");
}
arg1 = (int (*)[4])(argp1);
...
}
Searches for multi-argument typemaps are not mentioned unless a matching multi-argument typemap does actually exist. For example, the output for the code in the earlier multi-arguments section is as follows:
除非確實存在匹配的多引數型別映射,否則不涉及搜索多引數型別映射,例如,較早的多引數章節中的代碼輸出如下:
...
example.h:39: Searching for a suitable 'in' typemap for: char *buffer
Looking for: char *buffer
Multi-argument typemap found...
Using: %typemap(in) (char *buffer, int len)
...
The second option for debugging is -debug-tmused and this displays the typemaps used. This option is a less verbose version of the -debug-tmsearch option as it only displays each successfully found typemap on a separate single line. The output displays the type, and name if present, the typemap method in brackets and then the actual typemap used in the same simplified format output by the -debug-tmsearchoption. Below is the output for the example code at the start of this section on debugging.
除錯的第二個選項是
-debug-tmused,它顯示了使用的型別映射,這個選項是-debug-tmsearch選項的一個不太冗長的版本,因為它只在單獨的一行上顯示每個成功找到的型別映射,輸出將顯示型別和名稱(如果存在),括號中的型別映射方法,然后顯示由-debug-tmsearch選項以相同簡化格式輸出的實際型別映射,以下是本節開始時有關除錯的示例代碼的輸出,
$ swig -perl -debug-tmused example.i
example.h:3: Typemap for Row4 rows[10] (in) : %typemap(in) SWIGTYPE []
example.h:3: Typemap for Row4 rows[10] (typecheck) : %typemap(typecheck) SWIGTYPE *
example.h:3: Typemap for Row4 rows[10] (freearg) : %typemap(freearg) SWIGTYPE []
example.h:3: Typemap for void foo (out) : %typemap(out) void
Now, consider the following interface file:
現在,考慮下面的介面檔案:
%module example
%{
void set_value(const char* val) {}
%}
%typemap(check) char *NON_NULL {
if (!$1) {
/* ... error handling ... */
}
}
// use default pointer handling instead of strings
%apply SWIGTYPE * { const char* val, const char* another_value }
%typemap(check) const char* val = char* NON_NULL;
%typemap(arginit, noblock=1) const char* val {
$1 = "";
}
void set_value(const char* val);
and the output debug:
輸出除錯結果:
swig -perl5 -debug-tmused example.i
example.i:21: Typemap for char const *val (arginit) : %typemap(arginit) char const *val
example.i:21: Typemap for char const *val (in) : %apply SWIGTYPE * { char const *val }
example.i:21: Typemap for char const *val (typecheck) : %apply SWIGTYPE * { char const *val }
example.i:21: Typemap for char const *val (check) : %typemap(check) char const *val = char *NON_NULL
example.i:21: Typemap for char const *val (freearg) : %apply SWIGTYPE * { char const *val }
example.i:21: Typemap for void set_value (out) : %typemap(out) void
The following observations about what is displayed can be noted (the same applies for -debug-tmsearch):
- The relevant typemap is shown, but for typemap copying, the appropriate
%typemapor%applyis displayed, for example, the "check" and "in" typemaps. - The typemap modifiers are not shown, eg the
noblock=1modifier in the "arginit" typemap. - The exact
%applystatement might look different to what is in the actual code. For example, theconst char* another_valueis not shown as it is not relevant here. Also the types may be displayed slightly differently -char const *and notconst char*.
可以注意到以下有關顯示內容的觀察結果(
-debug-tmsearch同樣適用):
- 顯示了相關的型別映射,但是對于復制型別映射,將顯示適當的
%typemap或%apply,例如,check和in型別映射,- 型別映射修飾符未顯示,例如,
arginit型別映射中的noblock = 1修飾符,- 確切的
%apply陳述句可能看起來與實際代碼不同,例如,未顯示const char* another_value,因為此處不相關,同樣,型別的顯示可能略有不同——char const*而不是const char*,
11.4 代碼生成規則
This section describes rules by which typemap code is inserted into the generated wrapper code.
本節描述將型別映射代碼插入到生成的包裝器代碼中的規則,
11.4.1 作用域
When a typemap is defined like this:
當型別映射定義如下:
%typemap(in) int {
$1 = PyInt_AsLong($input);
}
the typemap code is inserted into the wrapper function using a new block scope. In other words, the wrapper code will look like this:
使用新的塊作用域將型別映射代碼插入包裝器函式,換句話說,包裝器代碼將如下所示:
wrap_whatever() {
...
// Typemap code
{
arg1 = PyInt_AsLong(obj1);
}
...
}
Because the typemap code is enclosed in its own block, it is legal to declare temporary variables for use during typemap execution. For example:
因為型別映射代碼包含在其自己的塊中,所以宣告臨時變數供在型別映射執行期間使用是合法的,例如:
%typemap(in) short {
long temp; /* Temporary value */
if (Tcl_GetLongFromObj(interp, $input, &temp) != TCL_OK) {
return TCL_ERROR;
}
$1 = (short) temp;
}
Of course, any variables that you declare inside a typemap are destroyed as soon as the typemap code has executed (they are not visible to other parts of the wrapper function or other typemaps that might use the same variable names).
Occasionally, typemap code will be specified using a few alternative forms. For example:
當然,你在型別映射中宣告的任何變數都將在該型別映射代碼執行后立即銷毀(它們對于包裝器函式的其他部分,或其他可能使用相同變數名的型別映射不可見),
有時,會使用一些其他形式來指定型別映射代碼,例如:
%typemap(in) int "$1 = PyInt_AsLong($input);";
%typemap(in) int %{
$1 = PyInt_AsLong($input);
%}
%typemap(in, noblock=1) int {
$1 = PyInt_AsLong($input);
}
These three forms are mainly used for cosmetics--the specified code is not enclosed inside a block scope when it is emitted. This sometimes results in a less complicated looking wrapper function. Note that only the third of the three typemaps have the typemap code passed through the SWIG preprocessor.
這三種形式主要用于化妝——發出特定代碼時,指定代碼未包含在塊作用域內,有時這會導致看起來不太復雜的包裝器函式,請注意,三個型別映射中只有三分之一具有通過 SWIG 預處理程式傳遞的型別映射代碼,
11.4.2 宣告新的區域變數
Sometimes it is useful to declare a new local variable that exists within the scope of the entire wrapper function. A good example of this might be an application in which you wanted to marshal strings. Suppose you had a C++ function like this
有時,宣告存在于整個包裝器函式范圍內的新區域變數很有用,一個很好的例子就是你想在其中傳遞字串的應用程式,假設你有一個這樣的 C++ 函式
int foo(std::string *s);
and you wanted to pass a native string in the target language as an argument. For instance, in Perl, you wanted the function to work like this:
并且你想傳遞目標語言中的原生字串作為引數,例如,在 Perl 中,你希望函式像這樣作業:
$x = foo("Hello World");
To do this, you can't just pass a raw Perl string as the std::string * argument. Instead, you have to create a temporary std::string object, copy the Perl string data into it, and then pass a pointer to the object. To do this, simply specify the typemap with an extra parameter like this:
為此,你不能僅將原始 Perl 字串作為
std::string *引數傳遞,相反,你必須創建一個臨時的std::string物件,將 Perl 字串資料復制到其中,然后將指標傳遞給該物件,為此,只需使用如下額外引數指定型別映射:
%typemap(in) std::string * (std::string temp) {
unsigned int len;
char *s;
s = SvPV($input, len); /* Extract string data */
temp.assign(s, len); /* Assign to temp */
$1 = &temp; /* Set argument to point to temp */
}
In this case, temp becomes a local variable in the scope of the entire wrapper function. For example:
在這種情況下,
temp成為整個包裝器函式范圍內的區域變數,例如:
wrap_foo() {
std::string temp; // <--- Declaration of temp goes here
...
/* Typemap code */
{
...
temp.assign(s, len);
...
}
...
}
When you set temp to a value, it persists for the duration of the wrapper function and gets cleaned up automatically on exit.
It is perfectly safe to use more than one typemap involving local variables in the same declaration. For example, you could declare a function as :
當你將
temp設定為一個值時,它將在包裝器函式的整個程序中持續存在,并在退出時自動清除,在同一宣告中使用多個涉及區域變數的型別映射是絕對安全的,例如,你可以將一個函式宣告為:
void foo(std::string *x, std::string *y, std::string *z);
This is safely handled because SWIG actually renames all local variable references by appending an argument number suffix. Therefore, the generated code would actually look like this:
這是安全處理的,因為 SWIG 實際上通過附加引數編號后綴來重命名所有區域變數參考,因此,生成的代碼實際上將如下所示:
wrap_foo() {
int *arg1; /* Actual arguments */
int *arg2;
int *arg3;
std::string temp1; /* Locals declared in the typemap */
std::string temp2;
std::string temp3;
...
{
char *s;
unsigned int len;
...
temp1.assign(s, len);
arg1 = *temp1;
}
{
char *s;
unsigned int len;
...
temp2.assign(s, len);
arg2 = &temp2;
}
{
char *s;
unsigned int len;
...
temp3.assign(s, len);
arg3 = &temp3;
}
...
}
There is an exception: if the variable name starts with the _global_ prefix, the argument number is not appended. Such variables can be used throughout the generated wrapper function. For example, the above typemap could be rewritten to use _global_temp instead of temp and the generated code would then contain a single _global_temp variable instead of temp1, temp2 and temp3:
有一個例外:如果變數名以
_global_前綴開頭,則不附加引數編號,此類變數可在整個生成的包裝器函式中使用,例如,上面的型別映射可以重寫為使用_global_temp而不是temp,然后生成的代碼將包含單個_global_temp變數而不是temp1、temp2和temp3:
%typemap(in) std::string * (std::string _global_temp) {
... as above ...
}
Some typemaps do not recognize local variables (or they may simply not apply). At this time, only typemaps that apply to argument conversion support this (input typemaps such as the "in" typemap).
Note:
When declaring a typemap for multiple types, each type must have its own local variable declaration.
一些型別映射不能識別區域變數(或者它們可能根本不適用),目前,僅適用于引數轉換的型別映射支持此功能(輸入型別映射,例如
in型別映射),注意:
當宣告多個型別的型別映射時,每個型別必須具有自己的區域變數宣告,
%typemap(in) const std::string *, std::string * (std::string temp) // NO!
// only std::string * has a local variable
// const std::string * does not (oops)
....
%typemap(in) const std::string * (std::string temp), std::string * (std::string temp) // Correct
....
11.4.3 特殊變數
Within all typemaps, the following special variables are expanded. This is by no means a complete list as some target languages have additional special variables which are documented in the language specific chapters.
下列特殊變數是對所有型別映射的擴展,這絕不是一個完整的串列,因為某些目標語言具有額外的特殊變數,這些特殊變數記錄在目標語言的特定章節中,
| Variable | Meaning |
|---|---|
$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. |
Within the table, $n refers to a specific type within the typemap specification. For example, if you write this
在表中,
$n表示型別映射規范中的特定型別,例如,如果你撰寫此
%typemap(in) int *INPUT {
}
then $1 refers to int *INPUT. If you have a typemap like this,
那么
$1指向int *INPUT,如果你有如下型別映射,
%typemap(in) (int argc, char *argv[]) {
...
}
then $1 refers to int argc and $2 refers to char *argv[].
Substitutions related to types and names always fill in values from the actual code that was matched. This is useful when a typemap might match multiple C datatype. For example:
那么
$1指向int argc,$2指向char *argv[],與型別和名稱相關的替換總是填充匹配的實際代碼中的值,當一個型別映射可能匹配多個 C 資料型別時很有用,例如:
%typemap(in) int, short, long {
$1 = ($1_ltype) PyInt_AsLong($input);
}
In this case, $1_ltype is replaced with the datatype that is actually matched.
When typemap code is emitted, the C/C++ datatype of the special variables $1 and $2 is always an "ltype." An "ltype" is simply a type that can legally appear on the left-hand side of a C assignment operation. Here are a few examples of types and ltypes:
在這種情況下,將
$1_ltype替換為實際匹配的資料型別,當發出型別映射代碼時,特殊變數
$1和$2的 C/C++ 資料型別始終是ltype,ltype只是可以合法出現在 C 賦值操作左側的型別,以下是一些型別和ltypes的示例:
type ltype
------ ----------------
int int
const int int
const int * int *
int [4] int *
int [4][5] int (*)[5]
In most cases a ltype is simply the C datatype with qualifiers stripped off. In addition, arrays are converted into pointers.
Variables such as $&1_type and $*1_type are used to safely modify the type by removing or adding pointers. Although not needed in most typemaps, these substitutions are sometimes needed to properly work with typemaps that convert values between pointers and values.
If necessary, type related substitutions can also be used when declaring locals. For example:
在大多數情況下,
ltype只是帶有限定符的 C 資料型別,另外,陣列被轉換為指標,諸如
$&1_type和$*1_type之類的變數用于通過洗掉或添加指標來安全地修改型別,盡管在大多數型別映射中不需要,但是有時有時需要這些替換才能正確處理在指標和值之間轉換值的型別映射,如有必要,在宣告區域變數時也可以使用型別相關的替換,例如:
%typemap(in) int * ($*1_type temp) {
temp = PyInt_AsLong($input);
$1 = &temp;
}
There is one word of caution about declaring local variables in this manner. If you declare a local variable using a type substitution such as $1_ltype temp, it won't work like you expect for arrays and certain kinds of pointers. For example, if you wrote this,
以這種方式宣告區域變數有一個警告,如果你使用諸如
$1_ltype temp之類的型別替換宣告區域變數,它將無法像你期望的那樣使用陣列和某些型別的指標,例如,如果你撰寫了此代碼,
%typemap(in) int [10][20] {
$1_ltype temp;
}
then the declaration of temp will be expanded as
那么
temp的宣告將被擴展為
int (*)[20] temp;
This is illegal C syntax and won't compile. There is currently no straightforward way to work around this problem in SWIG due to the way that typemap code is expanded and processed. However, one possible workaround is to simply pick an alternative type such as void * and use casts to get the correct type when needed. For example:
這是非法的 C 語法,不會被編譯,由于型別映射代碼的擴展和處理方式,當前在 SWIG 中沒有解決此問題的簡單方法,然而,一種可能的解決方法是簡單地選擇一種替代型別,例如
void *,并在需要時使用強制型別轉換來獲取正確的型別,例如:
%typemap(in) int [10][20] {
void *temp;
...
(($1_ltype) temp)[i][j] = x; /* set a value */
...
}
Another approach, which only works for arrays is to use the $1_basetype substitution. For example:
另一種只對陣列有效的方法是使用
$1_basetype替換,例如:
%typemap(in) int [10][20] {
$1_basetype temp[10][20];
...
temp[i][j] = x; /* set a value */
...
}
11.4.4 特殊變數宏
Special variable macros are like macro functions in that they take one or more input arguments which are used for the macro expansion. They look like macro/function calls but use the special variable $ prefix to the macro name. Note that unlike normal macros, the expansion is not done by the preprocessor, it is done during the SWIG parsing/compilation stages. The following special variable macros are available across all language modules.
特殊變數宏就像宏函式一樣,它們接受一個或多個用于宏擴展的輸入引數,它們看起來像是宏或函式呼叫,但是在宏名稱中使用特殊變數
$前綴,請注意,與普通宏不同,擴展不是由前處理器完成的,而是在 SWIG 決議或編譯階段完成的,以下特殊變數宏可在所有語言模塊中使用,
11.4.4.1 $descriptor(type)
This macro expands into the type descriptor structure for any C/C++ type specified in type. It behaves like the $1_descriptor special variable described above except that the type to expand is taken from the macro argument rather than inferred from the typemap type. For example, $descriptor(std::vector<int> *) will expand into SWIGTYPE_p_std__vectorT_int_t. This macro is mostly used in the scripting target languages and is demonstrated later in the Run-time type checker usage section.
這個宏擴展為
type中指定的任何 C/C++ 型別的型別描述符結構,它的行為類似于上述的$1_descriptor特殊變數,不同之處在于要擴展的型別是從宏引數中獲取的,而不是從型別映射型別中推斷出來的,例如,$descriptor(std::vector<int> *)將擴展為SWIGTYPE_p_std__vectorT_int_t,該宏主要用于腳本化目標語言,稍后在運行時型別檢查器用法章節中進行演示,
11.4.4.2 $typemap(method, typepattern)
This macro uses the pattern matching rules described earlier to lookup and then substitute the special variable macro with the code in the matched typemap. The typemap to search for is specified by the arguments, where method is the typemap method name and typepattern is a type pattern as per the %typemap specification in the Defining a typemap section.
The special variables within the matched typemap are expanded into those for the matched typemap type, not the typemap within which the macro is called. In practice, there is little use for this macro in the scripting target languages. It is mostly used in the target languages that are statically typed as a way to obtain the target language type given the C/C++ type and more commonly only when the C++ type is a template parameter.
The example below is for C# only and uses some typemap method names documented in the C# chapter, but it shows some of the possible syntax variations.
此宏使用前面描述的模式匹配規則查找,然后用匹配的型別映射中的代碼替換特殊變數宏,根據引數指定要搜索的型別映射,其中
method是型別映射方法名稱,而typepattern是型別模式,正如定義型別映射章節的%typemap規則,匹配的型別映射中的特殊變數將擴展為匹配的型別映射型別的特殊變數,而不是其中呼叫宏的型別映射,實際上,在腳本目標語言中,此宏很少使用,它通常用在目標語言中,這些目標語言是靜態型別化的,以便在給定 C/C++ 型別的情況下獲取目標語言型別,并且更常見的情況是僅在 C++ 型別是模板引數時使用,
下面的示例僅適用于 C#,并使用了 C# 一章中記錄的某些型別映射方法名稱,但它顯示了一些可能的語法變體,
%typemap(cstype) unsigned long "uint"
%typemap(cstype) unsigned long bb "bool"
%typemap(cscode) BarClass %{
void foo($typemap(cstype, unsigned long aa) var1,
$typemap(cstype, unsigned long bb) var2,
$typemap(cstype, (unsigned long bb)) var3,
$typemap(cstype, unsigned long) var4)
{
// do something
}
%}
The result is the following expansion
結果是下列擴展
%typemap(cstype) unsigned long "uint"
%typemap(cstype) unsigned long bb "bool"
%typemap(cscode) BarClass %{
void foo(uint var1,
bool var2,
bool var3,
uint var4)
{
// do something
}
%}
11.4.5 特殊變數與型別映射屬性
As of SWIG-3.0.7 typemap attributes will also expand special variables and special variable macros.
Example usage showing the expansion in the 'out' attribute (C# specific) as well as the main typemap body:
從 SWIG-3.0.7 開始,型別映射屬性還將擴展特殊變數和特殊變數宏,
用法示例顯示
out屬性(特定于 C#)以及主要的型別映射主體中的擴展:
%typemap(ctype, out="$*1_ltype") unsigned int& "$*1_ltype"
is equivalent to the following as $*1_ltype expands to unsigned int:
與下面的等價,相當于
$*1_ltype擴展unsigned int:
%typemap(ctype, out="unsigned int") unsigned int& "unsigned int"
11.4.6 特殊變數聯合特殊變數宏
Special variables can also be used within special variable macros. The special variables are expanded before they are used in the special variable macros.
Consider the following C# typemaps:
特殊變數也可以在特殊變數宏中使用,在特殊變數宏中使用特殊變數之前,先對其進行擴展,
考慮以下 C# 型別映射:
%typemap(cstype) unsigned int "uint"
%typemap(cstype, out="$typemap(cstype, $*1_ltype)") unsigned int& "$typemap(cstype, $*1_ltype)"
Special variables are expanded first and hence the above is equivalent to:
特殊變數首先被擴展,因此以上等效于:
%typemap(cstype) unsigned int "uint"
%typemap(cstype, out="$typemap(cstype, unsigned int)") unsigned int& "$typemap(cstype, unsigned int)"
which then expands to:
然后擴展:
%typemap(cstype) unsigned int "uint"
%typemap(cstype, out="uint") unsigned int& "uint"
11.5 通用型別映射方法
The set of typemaps recognized by a language module may vary. However, the following typemap methods are nearly universal:
語言模塊識別的型別映射集可能有所不同,但是,以下型別映射方法幾乎是通用的:
11.5.1 in 型別映射
The "in" typemap is used to convert function arguments from the target language to C. For example:
in型別映射用于將函式引數從目標語言轉換為 C 語言,例如:
%typemap(in) int {
$1 = PyInt_AsLong($input);
}
The following special variables are available:
以下特殊變數可用:
$input - Input object holding value to be converted.
$symname - Name of function/method being wrapped
This is probably the most commonly redefined typemap because it can be used to implement customized conversions.
In addition, the "in" typemap allows the number of converted arguments to be specified. The numinputs attributes facilitates this. For example:
這可能是最常見的重新定義的型別映射,因為它可用于實作自定義轉換,
另外,
in型別映射允許指定轉換引數的數量,numinputs屬性有助于實作這一點,例如:
// Ignored argument.
%typemap(in, numinputs=0) int *out (int temp) {
$1 = &temp;
}
At this time, only zero or one arguments may be converted. When numinputs is set to 0, the argument is effectively ignored and cannot be supplied from the target language. The argument is still required when making the C/C++ call and the above typemap shows the value used is instead obtained from a locally declared variable called temp. Usually numinputs is not specified, whereupon the default value is 1, that is, there is a one to one mapping of the number of arguments when used from the target language to the C/C++ call. Multi-argument typemaps provide a similar concept where the number of arguments mapped from the target language to C/C++ can be changed for multiple adjacent C/C++ arguments.
Compatibility note: Specifying numinputs=0 is the same as the old "ignore" typemap.
此時,只能轉換零個或一個引數,當
numinputs設定為 0 時,該引數將被有效忽略,并且無法從目標語言中提供,進行 C/C++ 呼叫時,仍然需要該引數,并且上面的型別映射顯示所使用的值是從區域宣告的名為temp的變數中獲取的,通常不指定numinputs,因此默認值為1,即從目標語言到 C/C++ 呼叫使用時,引數數量是一對一的映射,多引數型別映射提供了類似的概念,其中可以為多個相鄰的 C更改從目標語言映射到 C/C++ 的引數數量 / C++ 引數,注意兼容性:指定
numinputs = 0與舊的ignore型別映射相同,
11.5.2 typecheck 型別映射
The "typecheck" typemap is used to support overloaded functions and methods. It merely checks an argument to see whether or not it matches a specific type. For example:
typecheck型別映射用于支持多載的函式和方法,它僅檢查引數以查看其是否與特定型別匹配,例如:
%typemap(typecheck, precedence=SWIG_TYPECHECK_INTEGER) int {
$1 = PyInt_Check($input) ? 1 : 0;
}
For typechecking, the $1 variable is always a simple integer that is set to 1 or 0 depending on whether or not the input argument is the correct type. Set to 1 if the input argument is the correct type otherwise set to 0.
If you define new "in" typemaps and your program uses overloaded methods, you should also define a collection of "typecheck" typemaps. More details about this follow in the Typemaps and overloading section.
對于型別檢查,
$1變數始終是一個簡單整數,根據輸入引數是否為正確的型別將其設定為1或0,如果輸入引數是正確的型別,則設定為1,否則設定為0,如果你定義新的
in型別映射,并且你的程式使用多載方法,則還應該定義typecheck型別映射的集合,有關此問題的更多詳細資訊,請參見型別映射與多載章節,
11.5.3 out 型別映射
The "out" typemap is used to convert function/method return values from C into the target language. For example:
out型別映射用于將函式或方法的回傳值從 C 轉換為目標語言,例如:
%typemap(out) int {
$result = PyInt_FromLong($1);
}
The following special variables are available.
以下特殊變數可用:
$result - Result object returned to target language.
$symname - Name of function/method being wrapped
The "out" typemap supports an optional attribute flag called "optimal". This is for code optimisation and is detailed in the Optimal code generation when returning by value section.
out型別映射支持名為optimal的可選屬性標志,這是用于代碼優化的,在按值回傳時的最佳代碼生成章節中進行了詳細說明,
11.5.4 arginit 型別映射
The "arginit" typemap is used to set the initial value of a function argument--before any conversion has occurred. This is not normally necessary, but might be useful in highly specialized applications. For example:
在進行任何轉換之前,
arginit型別映射用于設定函式引數的初始值,通常這不是必需的,但在高度專業化的應用程式中可能很有用,例如:
// Set argument to NULL before any conversion occurs
%typemap(arginit) int *data {
$1 = NULL;
}
11.5.5 default 型別映射
The "default" typemap is used to turn an argument into a default argument. For example:
default型別映射用于將引數轉換為默認引數,例如:
%typemap(default) int flags {
$1 = DEFAULT_FLAGS;
}
...
int foo(int x, int y, int flags);
The primary use of this typemap is to either change the wrapping of default arguments or specify a default argument in a language where they aren't supported (like C). Target languages that do not support optional arguments, such as Java and C#, effectively ignore the value specified by this typemap as all arguments must be given.
Once a default typemap has been applied to an argument, all arguments that follow must have default values. See the Default/optional arguments section for further information on default argument wrapping.
此型別映射的主要用途是更改默認引數的包裝,或為不支持默認引數的語言(例如 C)指定默認引數,不支持可選引數的目標語言(例如 Java 和 C#)實際上會忽略此型別映射所指定的值,因為必須提供所有引數,
將默認型別映射應用于引數后,后面的所有引數都必須具有默認值,有關默認引數包裝的更多資訊,請參見默認與可選引數章節,
11.5.6 check 型別映射
The "check" typemap is used to supply value checking code during argument conversion. The typemap is applied after arguments have been converted. For example:
check型別映射用于在引數轉換期間提供值檢查代碼,型別引數是在引數轉換之后應用的,例如:
%typemap(check) int positive {
if ($1 <= 0) {
SWIG_exception(SWIG_ValueError, "Expected positive value.");
}
}
11.5.7 argout 型別映射
The "argout" typemap is used to return values from arguments. This is most commonly used to write wrappers for C/C++ functions that need to return multiple values. The "argout" typemap is almost always combined with an "in" typemap---possibly to ignore the input value. For example:
argout型別映射用于從引數回傳值,這最常用于為需要回傳多個值的 C/C++ 函式撰寫包裝器,argout型別映射幾乎總是與in型別映射結合使用——可能會忽略輸入值,例如:
/* Set the input argument to point to a temporary variable */
%typemap(in, numinputs=0) int *out (int temp) {
$1 = &temp;
}
%typemap(argout) int *out {
// Append output value $1 to $result
...
}
The following special variables are available.
可以使用下列特殊變數,
$result - Result object returned to target language.
$input - The original input object passed.
$symname - Name of function/method being wrapped
The code supplied to the "argout" typemap is always placed after the "out" typemap. If multiple return values are used, the extra return values are often appended to return value of the function.
See the typemaps.i library file for examples.
提供給
argout型別映射的代碼始終放置在out型別映射之后,如果使用多個回傳值,則通常會將多余的回傳值附加到函式的回傳值上,有關示例,請參見
typemaps.i庫檔案,
11.5.8 freearg 型別映射
The "freearg" typemap is used to cleanup argument data. It is only used when an argument might have allocated resources that need to be cleaned up when the wrapper function exits. The "freearg" typemap usually cleans up argument resources allocated by the "in" typemap. For example:
freearg型別映射用于清除引數資料,僅當引數可能分配了包裝器函式退出時需要清除的資源時才使用它,通常,freearg型別映射會清除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);
}
The "freearg" typemap inserted at the end of the wrapper function, just before control is returned back to the target language. This code is also placed into a special variable $cleanup that may be used in other typemaps whenever a wrapper function needs to abort prematurely.
在控制元件回傳到目標語言之前,將
freearg型別映射插入包裝器函式的末尾,這段代碼也被放入一個特殊的變數$cleanup中,只要包裝器函式需要提前中止,該變數就可以在其他型別映射中使用,
11.5.9 newfree 型別映射
The "newfree" typemap is used in conjunction with the %newobject directive and is used to deallocate memory used by the return result of a function. For example:
newfree型別映射與%newobject指令一起使用,用于釋放函式回傳結果使用的記憶體,例如:
%typemap(newfree) string * {
delete $1;
}
%typemap(out) string * {
$result = PyString_FromString($1->c_str());
}
...
%newobject foo;
...
string *foo();
See Object ownership and %newobject for further details.
更多細節請查看物件所有權和
%newobject章節,
11.5.10 ret 型別映射
The "ret" typemap is not used very often, but can be useful for anything associated with the return type, such as resource management, return value error checking, etc. Usually this can all be done in the "out" typemap, but sometimes it is handy to use the "out" typemap code untouched and add to the generated code using the code in the "ret" typemap. One such case is memory clean up. For example, a stringheap_t type is defined indicating that the returned memory must be deleted and a string_t type is defined indicating that the returned memory must not be deleted.
ret型別映射不是很經常使用,但是對于與回傳型別相關的任何事情(例如資源管理,回傳值錯誤檢查等)都很有用,通常都可以在out型別映射中完成,但是有時方便地使用未修改的out型別映射代碼,并使用ret型別映射中的代碼添加到生成的代碼中,一種這樣的情況是記憶體清理,例如,定義了stringheap_t型別,指示必須洗掉回傳的記憶體,定義string_t型別,指示必須洗掉回傳的記憶體,
%typemap(ret) stringheap_t %{
free($1);
%}
typedef char * string_t;
typedef char * stringheap_t;
string_t MakeString1();
stringheap_t MakeString2();
The "ret" typemap above will only be used for MakeString2, but both functions will use the default "out" typemap for char * provided by SWIG. The code above would ensure the appropriate memory is freed in all target languages as the need to provide custom "out" typemaps (which involve target language specific code) is not necessary.
This approach is an alternative to using the "newfree" typemap and %newobject as there is no need to list all the functions that require the memory cleanup, it is purely done on types.
上面的
ret型別映射將僅用于MakeString2,但是兩個函式都將使用 SWIG 提供的char *的默認out型別映射,上面的代碼將確保在所有目標語言中釋放適當的記憶體,因為不需要提供自定義的out型別映射(涉及目標語言特定的代碼),這種方法是使用
newfree型別映射和%newobject的一種替代方法,因為不需要列出所有需要記憶體清理的功能,它完全是在型別上完成的,
11.5.11 memberin 型別映射
The "memberin" typemap is used to copy data from an already converted input value into a structure member. It is typically used to handle array members and other special cases. For example:
memberin型別映射用于將資料從已經轉換的輸入值復制到結構體成員中,它通常用于處理陣列成員和其他特殊情況,例如:
%typemap(memberin) int [4] {
memmove($1, $input, 4*sizeof(int));
}
It is rarely necessary to write "memberin" typemaps---SWIG already provides a default implementation for arrays, strings, and other objects.
幾乎沒有必要撰寫
memberin型別映射——SWIG 已經為陣列、字串和其他物件提供了默認實作,
11.5.12 varin 型別映射
The "varin" typemap is used to convert objects in the target language to C for the purposes of assigning to a C/C++ global variable. This is implementation specific.
varin型別映射用于將目標語言中的物件轉換為 C,以分配給 C/C++ 全域變數,這是特定于實作的,
11.5.13 varout 型別映射
The "varout" typemap is used to convert a C/C++ object to an object in the target language when reading a C/C++ global variable. This is implementation specific.
讀取 C/C++ 全域變數時,
varout型別映射用于將 C/C++ 物件轉換為目標語言中的物件,這是特定于實作的,
11.5.14 throws 型別映射
The "throws" typemap is only used when SWIG parses a C++ method with an exception specification or has the %catches feature attached to the method. It provides a default mechanism for handling C++ methods that have declared the exceptions they will throw. The purpose of this typemap is to convert a C++ exception into an error or exception in the target language. It is slightly different to the other typemaps as it is based around the exception type rather than the type of a parameter or variable. For example:
僅當 SWIG 決議具有例外規范的 C++ 方法,或將
%catches功能附加到該方法時才使用throw型別映射,它提供了一種默認機制來處理宣告了將要拋出的例外的 C++ 方法,此型別映射的目的是將 C++ 例外轉換為目標語言中的錯誤或例外,它與其他型別映射略有不同,因為它基于例外型別而不是引數或變數的型別,例如:
%typemap(throws) const char * %{
PyErr_SetString(PyExc_RuntimeError, $1);
SWIG_fail;
%}
void bar() throw (const char *);
As can be seen from the generated code below, SWIG generates an exception handler with the catch block comprising the "throws" typemap content.
從下面的生成代碼中可以看出,SWIG 生成帶有
catch塊的例外處理程式,該catch塊包含throw型別映射內容,
...
try {
bar();
}
catch(char const *_e) {
PyErr_SetString(PyExc_RuntimeError, _e);
SWIG_fail;
}
...
Note that if your methods do not have an exception specification yet they do throw exceptions, SWIG cannot know how to deal with them. For a neat way to handle these, see the Exception handling with %exception section.
請注意,如果你的方法沒有例外規范,但它們確實會引發例外,則 SWIG 無法知道如何處理它們,有關處理這些錯誤的巧妙方法,請參閱使用
%exception處理例外章節,
11.6 一些型別映射示例
This section contains a few examples. Consult language module documentation for more examples.
本節包含一些示例,有關更多示例,請查閱語言模塊的檔案,
11.6.1 陣列的型別映射
A common use of typemaps is to provide support for C arrays appearing both as arguments to functions and as structure members.
For example, suppose you had a function like this:
型別映射的一種常見用法是為 C 陣列提供支持,這些 C 陣列既作為函式的引數出現,又作為結構體成員出現,
例如,假設你具有如下函式:
void set_vector(int type, float value[4]);
If you wanted to handle float value[4] as a list of floats, you might write a typemap similar to this:
如果你想將
float value[4]作為一列浮點數表處理,則可以撰寫類似于以下內容的型別映射:
%typemap(in) float value[4] (float temp[4]) {
int i;
if (!PySequence_Check($input)) {
PyErr_SetString(PyExc_ValueError, "Expected a sequence");
SWIG_fail;
}
if (PySequence_Length($input) != 4) {
PyErr_SetString(PyExc_ValueError, "Size mismatch. Expected 4 elements");
SWIG_fail;
}
for (i = 0; i < 4; i++) {
PyObject *o = PySequence_GetItem($input, i);
if (PyNumber_Check(o)) {
temp[i] = (float) PyFloat_AsDouble(o);
} else {
PyErr_SetString(PyExc_ValueError, "Sequence elements must be numbers");
SWIG_fail;
}
}
$1 = temp;
}
In this example, the variable temp allocates a small array on the C stack. The typemap then populates this array and passes it to the underlying C function.
When used from Python, the typemap allows the following type of function call:
在這個例子中,變數
temp在 C 堆疊上分配了一個小陣列,然后,型別映射將填充此陣列,并將其傳遞給基礎 C 函式,當從 Python 使用時,型別映射允許以下型別的函式呼叫:
>>> set_vector(type, [ 1, 2.5, 5, 20 ])
If you wanted to generalize the typemap to apply to arrays of all dimensions you might write this:
如果要泛化型別映射以應用于所有維度的陣列,則可以這樣撰寫:
%typemap(in) float value[ANY] (float temp[$1_dim0]) {
int i;
if (!PySequence_Check($input)) {
PyErr_SetString(PyExc_ValueError, "Expected a sequence");
SWIG_fail;
}
if (PySequence_Length($input) != $1_dim0) {
PyErr_SetString(PyExc_ValueError, "Size mismatch. Expected $1_dim0 elements");
SWIG_fail;
}
for (i = 0; i < $1_dim0; i++) {
PyObject *o = PySequence_GetItem($input, i);
if (PyNumber_Check(o)) {
temp[i] = (float) PyFloat_AsDouble(o);
} else {
PyErr_SetString(PyExc_ValueError, "Sequence elements must be numbers");
SWIG_fail;
}
}
$1 = temp;
}
In this example, the special variable $1_dim0 is expanded with the actual array dimensions. Multidimensional arrays can be matched in a similar manner. For example:
在這個例子中,特殊變數
$1_dim0被擴展為實際的陣列維度,多維陣列可以類似的方式進行匹配,例如:
%typemap(in) float matrix[ANY][ANY] (float temp[$1_dim0][$1_dim1]) {
... convert a 2d array ...
}
For large arrays, it may be impractical to allocate storage on the stack using a temporary variable as shown. To work with heap allocated data, the following technique can be used.
對于大型陣列,使用所示的臨時變數在堆疊上分配存盤可能不切實際,要使用堆分配的資料,可以使用以下技術,
%typemap(in) float value[ANY] {
int i;
if (!PySequence_Check($input)) {
PyErr_SetString(PyExc_ValueError, "Expected a sequence");
SWIG_fail;
}
if (PySequence_Length($input) != $1_dim0) {
PyErr_SetString(PyExc_ValueError, "Size mismatch. Expected $1_dim0 elements");
SWIG_fail;
}
$1 = (float *) malloc($1_dim0*sizeof(float));
for (i = 0; i < $1_dim0; i++) {
PyObject *o = PySequence_GetItem($input, i);
if (PyNumber_Check(o)) {
$1[i] = (float) PyFloat_AsDouble(o);
} else {
free($1);
PyErr_SetString(PyExc_ValueError, "Sequence elements must be numbers");
SWIG_fail;
}
}
}
%typemap(freearg) float value[ANY] {
if ($1) free($1);
}
In this case, an array is allocated using malloc. The freearg typemap is then used to release the argument after the function has been called.
Another common use of array typemaps is to provide support for array structure members. Due to subtle differences between pointers and arrays in C, you can't just "assign" to a array structure member. Instead, you have to explicitly copy elements into the array. For example, suppose you had a structure like this:
在這種情況下,使用
malloc分配陣列,然后,在呼叫函式后,使用freearg型別映射釋放引數,陣列型別映射的另一個常見用途是為陣列結構體成員提供支持,由于 C 語言中的指標和陣列之間存在細微的差異,因此你不能只是“分配”給陣列結構體成員,相反,你必須將元素顯式復制到陣列中,例如,假設你具有這樣的結構體:
struct SomeObject {
float value[4];
...
};
When SWIG runs, it won't produce any code to set the vec member. You may even get a warning message like this:
SWIG 運行時,不會產生任何代碼來設定
vec成員,你甚至可能收到以下警告訊息:
$ swig -python example.i
example.i:10: Warning 462: Unable to set variable of type float [4].
These warning messages indicate that SWIG does not know how you want to set the vec field.
To fix this, you can supply a special "memberin" typemap like this:
這些警告訊息表明 SWIG 不知道你如何設定
vec欄位,要解決此問題,可以提供一個特殊的
memberin型別映射,如下所示:
%typemap(memberin) float [ANY] {
int i;
for (i = 0; i < $1_dim0; i++) {
$1[i] = $input[i];
}
}
The memberin typemap is used to set a structure member from data that has already been converted from the target language to C. In this case, $input is the local variable in which converted input data is stored. This typemap then copies this data into the structure.
When combined with the earlier typemaps for arrays, the combination of the "in" and "memberin" typemap allows the following usage:
memberin型別映射用于從已經從目標語言轉換為 C 的資料中設定結構體成員,在這種情況下,$input是區域變數,用于存盤轉換后的輸入資料,然后,此型別映射將此資料復制到結構體中,當與早期的陣列型別映射結合使用時,
in和memberin型別映射的組合允許以下用法:
>>> s = SomeObject()
>>> s.x = [1, 2.5, 5, 10]
Related to structure member input, it may be desirable to return structure members as a new kind of object. For example, in this example, you will get very odd program behavior where the structure member can be set nicely, but reading the member simply returns a pointer:
與結構體成員輸入有關,可能希望將結構體成員作為一種新的物件回傳,例如,在此示例中,你將獲得非常奇怪的程式行為,可以很好地設定結構體成員,但是讀取成員僅回傳一個指標:
>>> s = SomeObject()
>>> s.x = [1, 2.5, 5, 10]
>>> print s.x
_1008fea8_p_float
>>>
To fix this, you can write an "out" typemap. For example:
要修正的話,你可以使用
out型別映射,例如:
%typemap(out) float [ANY] {
int i;
$result = PyList_New($1_dim0);
for (i = 0; i < $1_dim0; i++) {
PyObject *o = PyFloat_FromDouble((double) $1[i]);
PyList_SetItem($result, i, o);
}
}
Now, you will find that member access is quite nice:
現在,你可以發現成員訪問變的相當正常:
>>> s = SomeObject()
>>> s.x = [1, 2.5, 5, 10]
>>> print s.x
[ 1, 2.5, 5, 10]
Compatibility Note: SWIG1.1 used to provide a special "memberout" typemap. However, it was mostly useless and has since been eliminated. To return structure members, simply use the "out" typemap.
注意兼容性:SWIG1.1 過去提供特殊的
memberout型別映射,但是,它幾乎沒有用,因此已被淘汰,要回傳結構體成員,只需使用out型別映射,
11.6.2 用型別映射的實作限制
One particularly interesting application of typemaps is the implementation of argument constraints. This can be done with the "check" typemap. When used, this allows you to provide code for checking the values of function arguments. For example:
型別映射的一個有趣應用是實作引數限制,這可以用
check型別映射做到,型別映射允許你提供代碼以檢查函式引數的值,例如:
%module math
%typemap(check) double posdouble {
if ($1 < 0) {
croak("Expecting a positive number");
}
}
...
double sqrt(double posdouble);
This provides a sanity check to your wrapper function. If a negative number is passed to this function, a Perl exception will be raised and your program terminated with an error message.
This kind of checking can be particularly useful when working with pointers. For example:
這為包裝器函式提供了完整性檢查,如果將負數傳遞給此函式,則會引發 Perl 例外,并且你的程式終止并顯示錯誤訊息,
在使用指標時,這種檢查特別有用,例如:
%typemap(check) Vector * {
if ($1 == 0) {
PyErr_SetString(PyExc_TypeError, "NULL Pointer not allowed");
SWIG_fail;
}
}
will prevent any function involving a Vector * from accepting a NULL pointer. As a result, SWIG can often prevent a potential segmentation faults or other run-time problems by raising an exception rather than blindly passing values to the underlying C/C++ program.
會阻止任何涉及
Vector *的函式接受空指標,最終,SWIG 通常可以通過引發例外,而不是將值盲目地傳遞給底層 C/C++ 程式來防止潛在的分段錯誤或其他運行時問題,
11.7 多目標語言的型別映射
The code within typemaps is usually language dependent, however, many target languages support the same typemaps. In order to distinguish typemaps across different languages, the preprocessor should be used. For example, the "in" typemap for Perl and Ruby could be written as:
型別映射中的代碼通常取決于語言,但是,許多目標語言都支持相同的型別映射,為了區分不同語言之間的型別映射,應使用前處理器,例如,Perl 和 Ruby 的
in型別映射可以寫為:
#if defined(SWIGPERL)
%typemap(in) int "$1 = ($1_ltype) SvIV($input);"
#elif defined(SWIGRUBY)
%typemap(in) int "$1 = NUM2INT($input);"
#else
#warning no "in" typemap defined
#endif
The full set of language specific macros is defined in the Conditional Compilation section. The example above also shows a common approach of issuing a warning for an as yet unsupported language.
Compatibility note: In SWIG-1.1 different languages could be distinguished with the language name being put within the %typemap directive, for example, %typemap(ruby, in) int "$1 = NUM2INT($input);".
在條件編譯章節中定義了特定于語言的完整宏集合,上面的示例還顯示了針對尚不支持的語言發出警告的常見方法,
注意兼容性:在 SWIG-1.1中,可以通過在
%typemap指令中放入語言名稱來區分不同的語言,例如,%typemap(ruby, in) int "$1 = NUM2INT($input);",
11.8 回傳值時的最優代碼生成
The "out" typemap is the main typemap for return types. This typemap supports an optional attribute flag called "optimal", which is for reducing temporary variables and the amount of generated code, thereby giving the compiler the opportunity to use return value optimization for generating faster executing code. It only really makes a difference when returning objects by value and has some limitations on usage, as explained later on.
When a function returns an object by value, SWIG generates code that instantiates the default type on the stack then assigns the value returned by the function call to it. A copy of this object is then made on the heap and this is what is ultimately stored and used from the target language. This will be clearer considering an example. Consider running the following code through SWIG:
out型別映射是回傳型別的主要型別映射,此型別映射支持一個稱為optimal的可選屬性標志,該標志用于減少臨時變數和所生成的代碼量,從而使編譯器有機會使用回傳值優化來生成執行速度更快的代碼,如后面所述,只有在按值回傳物件時,它才真正有所不同,并且在用法上有一些限制,當函式按值回傳物件時,SWIG 會生成代碼,該代碼實體化堆疊上的默認型別,然后將函式呼叫回傳的值分配給它,然后在堆上創建此物件的副本,這是最終從目標語言存盤和使用的物件,考慮一個例子,這將更加清楚,考慮通過 SWIG 運行以下代碼:
%typemap(out) SWIGTYPE %{
$result = new $1_ltype((const $1_ltype &)$1);
%}
%inline %{
#include <iostream>
using namespace std;
struct XX {
XX() { cout << "XX()" << endl; }
XX(int i) { cout << "XX(" << i << ")" << endl; }
XX(const XX &other) { cout << "XX(const XX &)" << endl; }
XX & operator =(const XX &other) { cout << "operator=(const XX &)" << endl; return *this; }
~XX() { cout << "~XX()" << endl; }
static XX create() {
return XX(0);
}
};
%}
The "out" typemap shown is the default typemap for C# when returning objects by value. When making a call to XX::create() from C#, the output is as follows:
當按值回傳物件時,顯示的
out型別映射是 C# 的默認型別映射,從 C# 呼叫XX::create()時,輸出如下:
XX()
XX(0)
operator=(const XX &)
~XX()
XX(const XX &)
~XX()
~XX()
Note that three objects are being created as well as an assignment. Wouldn't it be great if the XX::create() method was the only time a constructor was called? As the method returns by value, this is asking a lot and the code that SWIG generates by default makes it impossible for the compiler to use return value optimisation (RVO). However, this is where the "optimal" attribute in the "out" typemap can help out. If the typemap code is kept the same and just the "optimal" attribute specified like this:
請注意,正在創建三個物件以及一個分配,如果唯一呼叫建構式的方法是
XX::create()方法,那不是很好嗎?由于該方法按值回傳,因此要求很多,而 SWIG 默認生成的代碼使編譯器無法使用回傳值優化(RVO),但是,這是out型別映射中的optimal屬性可以提供幫助的地方,如果型別映射代碼保持相同,并且僅指定optimal屬性,如下所示:
%typemap(out, optimal="1") SWIGTYPE %{
$result = new $1_ltype((const $1_ltype &)$1);
%}
then when the code is run again, the output is simply:
再次運行代碼,輸出很簡單:
XX(0)
~XX()
How the "optimal" attribute works is best explained using the generated code. Without "optimal", the generated code is:
使用生成的代碼可以最好地解釋
optimal屬性的作業方式,如果沒有optimal,則生成的代碼為:
SWIGEXPORT void * SWIGSTDCALL CSharp_XX_create() {
void * jresult ;
XX result;
result = XX::create();
jresult = new XX((const XX &)result);
return jresult;
}
With the "optimal" attribute, the code is:
有了
optimal屬性,代碼為:
SWIGEXPORT void * SWIGSTDCALL CSharp_XX_create() {
void * jresult ;
jresult = new XX((const XX &)XX::create());
return jresult;
}
The major difference is the result temporary variable holding the value returned from XX::create() is no longer generated and instead the copy constructor call is made directly from the value returned by XX::create(). With modern compilers implementing RVO, the copy is not actually done, in fact the object is never created on the stack in XX::create() at all, it is simply created directly on the heap. In the first instance, the $1 special variable in the typemap is expanded into result. In the second instance, $1is expanded into XX::create() and this is essentially what the "optimal" attribute is telling SWIG to do.
The "optimal" attribute optimisation is not turned on by default as it has a number of restrictions. Firstly, some code cannot be condensed into a simple call for passing into the copy constructor. One common occurrence is when %exception is used. Consider adding the following %exception to the example:
主要區別是
result臨時變數不再保存從XX::create()回傳的值,而是直接從XX::create()回傳的值進行復制建構式呼叫,使用實作 RVO 的現代編譯器,實際上并不會完成復制,實際上,該物件根本不會在XX::create()中的堆疊上創建,而只是在堆上直接創建,首先,將型別映射中的$1特殊變數擴展為result,在第二種情況下,將$1擴展為XX::create(),這實際上就是optimal屬性告訴 SWIG 要做的事情,默認情況下,
optimal屬性優化未啟用,因為它有許多限制,首先,某些代碼不能被精簡為傳遞給復制建構式的簡單呼叫,一種常見的情況是使用%exception,考慮在示例中添加以下%exception:
%exception XX::create() %{
try {
$action
} catch(const std::exception &e) {
cout << e.what() << endl;
}
%}
SWIG can detect when the "optimal" attribute cannot be used and will ignore it and in this case will issue the following warning:
SWIG 可以檢測到何時無法使用
optimal屬性,并將其忽略,在這種情況下,將發出以下警告:
example.i:28: Warning 474: Method XX::create() usage of the optimal attribute ignored
example.i:14: Warning 474: in the out typemap as the following cannot be used to generate
optimal code:
try {
result = XX::create();
} catch(const std::exception &e) {
cout << e.what() << endl;
}
It should be clear that the above code cannot be used as the argument to the copy constructor call, that is, for the $1 substitution.
Secondly, if the typemaps uses $1 more than once, then multiple calls to the wrapped function will be made. Obviously that is not very optimal. In fact SWIG attempts to detect this and will issue a warning something like:
應該清楚的是,上面的代碼不能用作復制建構式呼叫的引數,即不能用于
$1替換,其次,如果型別映射多次使用
$1,則將多次呼叫包裝器函式,顯然,這不是很理想,實際上,SWIG 會嘗試檢測到這一點,并將發出類似以下的警告:
example.i:21: Warning 475: Multiple calls to XX::create() might be generated due to
example.i:7: Warning 475: optimal attribute usage in the out typemap.
However, it doesn't always get it right, for example when $1 is within some commented out code.
但是,它并不總是正確,例如,當
$1在某些注釋掉的代碼中時,
11.9 多引數型別映射
So far, the typemaps presented have focused on the problem of dealing with single values. For example, converting a single input object to a single argument in a function call. However, certain conversion problems are difficult to handle in this manner. As an example, consider the example at the very beginning of this chapter:
到目前為止,所提供的型別映射已集中在處理單個值的問題上,例如,在函式呼叫中將單個輸入物件轉換為單引數,但是,某些轉換問題很難以這種方式處理,例如,請考慮本章開頭的示例:
int foo(int argc, char *argv[]);
Suppose that you wanted to wrap this function so that it accepted a single list of strings like this:
假設你想包裝此函式,以使其接受單個字串串列,如下所示:
>>> foo(["ale", "lager", "stout"])
To do this, you not only need to map a list of strings to char *argv[], but the value of int argc is implicitly determined by the length of the list. Using only simple typemaps, this type of conversion is possible, but extremely painful. Multi-argument typemaps help in this situation.
A multi-argument typemap is a conversion rule that specifies how to convert a single object in the target language to a set of consecutive function arguments in C/C++. For example, the following multi-argument maps perform the conversion described for the above example:
為此,你不僅需要將字串串列映射到
char *argv[],而且int argc的值由串列的長度隱式確定,僅使用簡單的型別映射,這種型別的轉換是可能的,但是非常痛苦,在這種情況下,多引數型別映射會有所幫助,多引數型別映射是一種轉換規則,它指定如何將目標語言中的單個物件轉換為 C/C++ 中的一組連續函式引數,例如,以下多引數映射執行上述示例中描述的轉換:
%typemap(in) (int argc, char *argv[]) {
int i;
if (!PyList_Check($input)) {
PyErr_SetString(PyExc_ValueError, "Expecting a list");
SWIG_fail;
}
$1 = PyList_Size($input);
$2 = (char **) malloc(($1+1)*sizeof(char *));
for (i = 0; i < $1; i++) {
PyObject *s = PyList_GetItem($input, i);
if (!PyString_Check(s)) {
free($2);
PyErr_SetString(PyExc_ValueError, "List items must be strings");
SWIG_fail;
}
$2[i] = PyString_AsString(s);
}
$2[i] = 0;
}
%typemap(freearg) (int argc, char *argv[]) {
if ($2) free($2);
}
/* Required for C++ method overloading */
%typecheck(SWIG_TYPECHECK_STRING_ARRAY) (int argc, char *argv[]) {
$1 = PyList_Check($input) ? 1 : 0;
}
A multi-argument map is always specified by surrounding the arguments with parentheses as shown. For example:
如上所示,總是通過用括號將引數括起來來指定多引數映射,例如:
%typemap(in) (int argc, char *argv[]) { ... }
Within the typemap code, the variables $1, $2, and so forth refer to each type in the map. All of the usual substitutions apply--just use the appropriate $1 or $2 prefix on the variable name (e.g., $2_type, $1_ltype, etc.)
Multi-argument typemaps always have precedence over simple typemaps and SWIG always performs longest-match searching. Therefore, you will get the following behavior:
在型別映射代碼中,變數
$1、$2等參考映射中的每種型別,所有通常的替換都適用——只需在變數名稱上使用適當的$1或$2前綴即可(例如$2_type、$1_ltype等)多引數型別映射始終優先于簡單型別映射,而 SWIG 始終執行最長匹配搜索,因此,你將得到以下行為:
%typemap(in) int argc { ... typemap 1 ... }
%typemap(in) (int argc, char *argv[]) { ... typemap 2 ... }
%typemap(in) (int argc, char *argv[], char *env[]) { ... typemap 3 ... }
int foo(int argc, char *argv[]); // Uses typemap 2
int bar(int argc, int x); // Uses typemap 1
int spam(int argc, char *argv[], char *env[]); // Uses typemap 3
It should be stressed that multi-argument typemaps can appear anywhere in a function declaration and can appear more than once. For example, you could write this:
應該強調的是,多引數型別映射可以出現在函式宣告中的任何位置,并且可以出現多次,例如,你可以這樣撰寫:
%typemap(in) (int scount, char *swords[]) { ... }
%typemap(in) (int wcount, char *words[]) { ... }
void search_words(int scount, char *swords[], int wcount, char *words[], int maxcount);
Other directives such as %apply and %clear also work with multi-argument maps. For example:
其他指令,例如
%apply和%clear也可以與多引數映射一起使用,例如:
%apply (int argc, char *argv[]) {
(int scount, char *swords[]),
(int wcount, char *words[])
};
...
%clear (int scount, char *swords[]), (int wcount, char *words[]);
...
Don't forget to also provide a suitable typemap for overloaded functions, such as %typecheck shown for foo above. This is only required if the function is overloaded in C++.
Although multi-argument typemaps may seem like an exotic, little used feature, there are several situations where they make sense. First, suppose you wanted to wrap functions similar to the low-level read() and write() system calls. For example:
不要忘記提供合適的多載函式的型別映射,例如上面為
foo顯示的%typecheck,僅當函式在 C++ 中多載時才需要,盡管多引數型別映射可能看起來像是一種奇特的、很少使用的功能,但在某些情況下它們是有意義的,首先,假設你想包裝類似于低級
read()和write()系統呼叫的函式,例如:
typedef unsigned int size_t;
int read(int fd, void *rbuffer, size_t len);
int write(int fd, void *wbuffer, size_t len);
As is, the only way to use the functions would be to allocate memory and pass some kind of pointer as the second argument---a process that might require the use of a helper function. However, using multi-argument maps, the functions can be transformed into something more natural. For example, you might write typemaps like this:
如此這樣,使用這些函式的唯一方法是分配記憶體并傳遞某種指標作為第二個引數,該程序可能需要使用輔助函式,但是,使用多引數映射可以將功能轉換為更自然的功能,例如,你可以這樣撰寫型別映射:
// typemap for an outgoing buffer
%typemap(in) (void *wbuffer, size_t len) {
if (!PyString_Check($input)) {
PyErr_SetString(PyExc_ValueError, "Expecting a string");
SWIG_fail;
}
$1 = (void *) PyString_AsString($input);
$2 = PyString_Size($input);
}
// typemap for an incoming buffer
%typemap(in) (void *rbuffer, size_t len) {
if (!PyInt_Check($input)) {
PyErr_SetString(PyExc_ValueError, "Expecting an integer");
SWIG_fail;
}
$2 = PyInt_AsLong($input);
if ($2 < 0) {
PyErr_SetString(PyExc_ValueError, "Positive integer expected");
SWIG_fail;
}
$1 = (void *) malloc($2);
}
// Return the buffer. Discarding any previous return result
%typemap(argout) (void *rbuffer, size_t len) {
Py_XDECREF($result); /* Blow away any previous result */
if (result < 0) { /* Check for I/O error */
free($1);
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
$result = PyString_FromStringAndSize($1, result);
free($1);
}
(note: In the above example, $result and result are two different variables. result is the real C datatype that was returned by the function. $result is the scripting language object being returned to the interpreter.).
Now, in a script, you can write code that simply passes buffers as strings like this:
(注意:在上面的示例中,
$result和result是兩個不同的變數,result是函式回傳的實際 C 資料型別,$result是要回傳到解釋器的腳本語言物件 ),現在,在腳本中,你可以撰寫簡單地將緩沖區作為字串傳遞的代碼,如下所示:
>>> f = example.open("Makefile")
>>> example.read(f, 40)
'TOP = ../..\nSWIG = $(TOP)/.'
>>> example.read(f, 40)
'./swig\nSRCS = example.c\nTARGET '
>>> example.close(f)
0
>>> g = example.open("foo", example.O_WRONLY | example.O_CREAT, 0644)
>>> example.write(g, "Hello world\n")
12
>>> example.write(g, "This is a test\n")
15
>>> example.close(g)
0
>>>
A number of multi-argument typemap problems also arise in libraries that perform matrix-calculations--especially if they are mapped onto low-level Fortran or C code. For example, you might have a function like this:
在執行矩陣計算的庫中,還會出現許多多引數型別映射問題,尤其是如果將它們映射到低級 Fortran 或 C 代碼上,例如,你可能具有以下函式:
int is_symmetric(double *mat, int rows, int columns);
In this case, you might want to pass some kind of higher-level object as an matrix. To do this, you could write a multi-argument typemap like this:
在這種情況下,你可能需要傳遞某種高級物件作為矩陣,為此,你可以撰寫一個如下所示的多引數型別映射:
%typemap(in) (double *mat, int rows, int columns) {
MatrixObject *a;
a = GetMatrixFromObject($input); /* Get matrix somehow */
/* Get matrix properties */
$1 = GetPointer(a);
$2 = GetRows(a);
$3 = GetColumns(a);
}
This kind of technique can be used to hook into scripting-language matrix packages such as Numeric Python. However, it should also be stressed that some care is in order. For example, when crossing languages you may need to worry about issues such as row-major vs. column-major ordering (and perform conversions if needed). Note that multi-argument typemaps cannot deal with non-consecutive C/C++ arguments; a workaround such as a helper function re-ordering the arguments to make them consecutive will need to be written.
這種技術可用于連接腳本語言矩陣包,例如 Numeric Python,但是,還應該強調,一定要謹慎,例如,在使用多種語言時,你可能需要擔心行優先與列優先的排序(并在需要時執行轉換),注意,多引數型別映射不能處理非連續的 C/C++ 引數,需要撰寫一種變通方法,例如輔助函式,將引數重新排序以使其連續,
11.10 型別映射警告
Warnings can be added to typemaps so that SWIG generates a warning message whenever the typemap is used. See the information in the issuing warnings section.
可以將警告添加到型別映射,以便每當使用型別映射時 SWIG 都會生成警告訊息,請參閱發布警告章節中的資訊,
11.11 型別映射片段
The primary purpose of fragments is to reduce code bloat that repeated use of typemap code can lead to. Fragments are snippets of code that can be thought of as code dependencies of a typemap. If a fragment is used by more than one typemap, then the snippet of code within the fragment is only generated once. Code bloat is typically reduced by moving typemap code into a support function and then placing the support function into a fragment.
For example, if you have a very long typemap
片段的主要目的是減少重復使用型別映射代碼可能導致的代碼膨脹,片段是代碼片段,可以將其視為型別映射的代碼依賴項,如果一個片段被多個型別映射使用,則該片段內的代碼片段僅生成一次,通常可以通過將型別映射代碼移入支持函式,然后將支持函式放入片段中來減少代碼膨脹,
例如,如果你的型別映射很長
%typemap(in) MyClass * {
MyClass *value = https://www.cnblogs.com/xuruilong100/p/0;
... many lines of marshalling code ...
$result = value;
}
the same marshalling code is often repeated in several typemaps, such as "in", "varin", "directorout", etc. SWIG copies the code for each argument that requires the typemap code, easily leading to code bloat in the generated code. To eliminate this, define a fragment that includes the common marshalling code:
相同的編組代碼通常在多個型別映射中重復,例如
in、varin、directorout等,SWIG 為需要該型別映射代碼的每個引數復制代碼,從而很容易導致所生成代碼中的代碼膨脹,為了消除這種情況,請定義一個包含通用編組代碼的片段:
%fragment("AsMyClass", "header") {
MyClass *AsMyClass(PyObject *obj) {
MyClass *value = https://www.cnblogs.com/xuruilong100/p/0;
... many lines of marshalling code ...
return value;
}
}
%typemap(in, fragment="AsMyClass") MyClass * {
$result = AsMyClass($input);
}
%typemap(varin, fragment="AsMyClass") MyClass * {
$result = AsMyClass($input);
}
When the "in" or "varin" typemaps for MyClass are required, the contents of the fragment called "AsMyClass" is added to the "header" section within the generated code, and then the typemap code is emitted. Hence, the method AsMyClass will be generated into the wrapper code before any typemap code that calls it.
To define a fragment you need a fragment name, a section name for generating the fragment code into, and the code itself. See Code insertion blocks for a full list of section names. Usually the section name used is "header". Different delimiters can be used:
當需要
MyClass的in或varin型別映射時,將名為AsMyClass的片段的內容添加到生成的代碼中的header部分,然后發出該型別映射代碼,因此,方法AsMyClass將在呼叫它的任何型別映射代碼之前生成到包裝器代碼中,要定義一個片段,你需要一個片段名稱,用于將片段代碼生成到其中的段名稱以及代碼本身,有關部分名稱的完整串列,請參見代碼插入塊,通常,使用的節名稱是
header,可以使用不同的定界符:
%fragment("my_name", "header") %{ ... %}
%fragment("my_name", "header") { ... }
%fragment("my_name", "header") " ... "
and these follow the usual preprocessing rules mentioned in the Preprocessing delimiters section. The following are some rules and guidelines for using fragments:
- A fragment is added to the wrapping code only once. When using the
MyClass *typemaps above and wrapping the method:
并且它們遵循預處理分隔符章節中提到的常規預處理規則,以下是使用片段的一些規則和準則:
- 一個片段僅被添加到包裝代碼一次,當使用上面的
MyClass *型別映射并包裝方法時:
void foo(MyClass *a, MyClass *b);
the generated code will look something like:
將會產生類似下面的代碼:
MyClass *AsMyClass(PyObject *obj) {
...
}
void _wrap_foo(...) {
....
arg1 = AsMyClass(obj1);
arg2 = AsMyClass(obj2);
...
foo(arg1, arg2);
}
even as there is duplicated typemap code to process both a and b, the AsMyClass method will be defined only once.
- A fragment should only be defined once. If there is more than one definition, the first definition is the one used. All other definitions are silently ignored. For example, if you have
即使存在重復的型別映射代碼來處理
a和b、AsMyClass方法也只會定義一次,
- 一個片段只能定義一次,如果有多個定義,則第一個定義是使用的定義,所有其他定義都被忽略,例如,如果你有
%fragment("AsMyClass", "header") { ...definition 1... }
....
%fragment("AsMyClass", "header") { ...definition 2... }
only the first definition is used. In this way you can override the default fragments in a SWIG library by defining your fragment before the library %include. Note that this behavior is the opposite to typemaps, where the last typemap defined/applied prevails. Fragments follow the first-in-first-out convention since they are intended to be global, while typemaps are intended to be locally specialized.
-
Fragment names cannot contain commas.
-
A fragment can use one or more additional fragments, for example:
僅使用第一個定義,這樣,你可以通過在
%include庫之前定義片段來覆寫 SWIG 庫中的默認片段,請注意,此行為與型別映射相反,后者以最后定義或應用的型別映射為準,片段遵循先進先出的約定,因為它們是全域的,而型別映射則是區域的,
- 片段名稱不能包含逗號,
- 一個片段可以使用一個或多個其他片段,例如:
%fragment("<limits.h>", "header") {
%#include <limits.h>
}
%fragment("AsMyClass", "header", fragment="<limits.h>") {
MyClass *AsMyClass(PyObject *obj) {
MyClass *value = https://www.cnblogs.com/xuruilong100/p/0;
... some marshalling code ...
if (ival < CHAR_MIN /*defined in */) {
...
} else {
...
}
...
return value;
}
}
in this case, when the "AsMyClass" fragment is emitted, it also triggers the inclusion of the "<limits.h>" fragment.
- A fragment can have dependencies on a number of other fragments, for example:
在這種情況下,發出
AsMyClass片段時,也會觸發包含<limits.h>片段,
- 一個片段可以依賴于許多其他片段,例如:
%fragment("bigfragment", "header", fragment="frag1", fragment="frag2", fragment="frag3") "";
When the "bigfragment" is used, the three dependent fragments "frag1", "frag2" and "frag3" are also pulled in. Note that as "bigframent" is empty (the empty string - ""), it does not add any code itself, but merely triggers the inclusion of the other fragments.
- A typemap can also use more than one fragment, but since the syntax is different, you need to specify the dependent fragments in a comma separated list. Consider:
當使用
bigfragment時,三個從屬片段frag1、frag2和frag3也被拉入,請注意,由于bigframent為空(空字串——""),因此不添加任何代碼本身,但僅觸發其他片段的包含,
- 一個型別映射也可以使用多個片段,但是由于語法不同,你需要在逗號分隔的串列中指定從屬片段,考慮:
%typemap(in, fragment="frag1, frag2, frag3") {...}
which is equivalent to:
等效于:
%typemap(in, fragment="bigfragment") {...}
when used with the "bigfragment" defined above.
- Finally, you can force the inclusion of a fragment at any point in the generated code as follows:
與上面定義的
bigfragment一起使用時,
- 最后,你可以按以下步驟在生成的代碼中的任何位置強制包含片段:
%fragment("bigfragment");
which is very useful inside a template class, for example.
Most readers will probably want to skip the next two sub-sections on advanced fragment usage unless a desire to really get to grips with some powerful but tricky macro and fragment usage that is used in parts of the SWIG typemap library.
例如,這在模板類內部非常有用,
除非希望真正掌握 SWIG 型別映射庫的某些部分中使用的某些功能強大但棘手的宏和片段用法,否則大多數讀者可能會希望跳過接下來的兩節有關高級片段用法的小節,
11.11.1 片段型別特化
Fragments can be type specialized. The syntax is as follows:
片段可以是型別特化,語法如下:
%fragment("name", "header") { ...a type independent fragment... }
%fragment("name"{type}, "header") { ...a type dependent fragment... }
where type is a C/C++ type. Like typemaps, fragments can also be used inside templates, for example:
其中,
type是 C/C++ 型別,像型別映射一樣,片段也可以在模板內部使用,例如:
template <class T>
struct A {
%fragment("incode"{A<T>}, "header") {
... 'incode' specialized fragment ...
}
%typemap(in, fragment="incode"{A<T>}) {
... here we use the 'type specialized' fragment "incode"{A<T>} ...
}
};
11.11.2 片段與自動型別映射特化
Since fragments can be type specialized, they can be elegantly used to specialize typemaps. For example, if you have something like:
由于片段可以是型別特化的,因此可以很好地用于特化型別映射,例如,如果你有以下內容:
%fragment("incode"{float}, "header") {
float in_method_float(PyObject *obj) {
...
}
}
%fragment("incode"{long}, "header") {
float in_method_long(PyObject *obj) {
...
}
}
// %my_typemaps macro definition
%define %my_typemaps(Type)
%typemap(in, fragment="incode"{Type}) Type {
value = https://www.cnblogs.com/xuruilong100/p/in_method_##Type(obj);
}
%enddef
%my_typemaps(float);
%my_typemaps(long);
then the proper "incode"{float} or "incode"{long} fragment will be used, and the in_method_floatand in_method_long methods will be called whenever the float or long types are used as input parameters.
This feature is used a lot in the typemaps shipped in the SWIG library for some scripting languages. The interested (or very brave) reader can take a look at the fragments.swg file shipped with SWIG to see this in action.
那么將使用正確的
"incode"{float}或"incode"{long}片段,并且每當使用float或long型別時,就會呼叫in_method_float和in_method_long方法 作為輸入引數,SWIG 庫附帶的型別映射中的某些腳本語言經常使用此功能,有興趣的(或非常勇敢的)讀者可以查看 SWIG 附帶的
fragments.swg檔案,以了解實際情況,
11.12 運行時型別檢查器
Most scripting languages need type information at run-time. This type information can include how to construct types, how to garbage collect types, and the inheritance relationships between types. If the language interface does not provide its own type information storage, the generated SWIG code needs to provide it.
Requirements for the type system:
- Store inheritance and type equivalence information and be able to correctly re-create the type pointer.
- Share type information between modules.
- Modules can be loaded in any order, regardless of actual type dependency.
- Avoid the use of dynamically allocated memory, and library/system calls in general.
- Provide a reasonably fast implementation, minimizing the lookup time for all language modules.
- Custom, language specific information can be attached to types.
- Modules can be unloaded from the type system.
大多數腳本語言在運行時都需要型別資訊,此型別資訊可以包括如何構造型別,如何垃圾回收型別,以及型別之間的繼承關系,如果語言介面不提供自己的型別資訊存盤,則生成的 SWIG 代碼需要提供它,
型別系統要求:
- 存盤繼承和型別等效資訊,并能夠正確地重新創建型別指標,
- 在模塊之間共享型別資訊,
- 模塊可以以任何順序加載,而不管實際的型別依賴性如何,
- 避免一般使用動態分配的記憶體和庫/系統呼叫,
- 提供合理快速的實施,以最小化所有語言模塊的查找時間,
- 自定義,特定于語言的資訊可以附加到型別上,
- 可以從型別系統中卸載模塊,
11.12.1 實作
The run-time type checker is used by many, but not all, of SWIG's supported target languages. The run-time type checker features are not required and are thus not used for statically typed languages such as Java and C#. The scripting and scheme based languages rely on it and it forms a critical part of SWIG's operation for these languages.
When pointers, arrays, and objects are wrapped by SWIG, they are normally converted into typed pointer objects. For example, an instance of Foo * might be a string encoded like this:
SWIG 支持的許多(但不是全部)目標語言都使用運行時型別檢查器,運行時型別檢查器功能不是必需的,因此不用于 Java 和 C# 之類的靜態型別語言,基于腳本和框架的語言都依賴它,并且它構成了 SWIG 對這些語言的操作的關鍵部分,
當指標,陣列和物件由 SWIG 包裝時,它們通常會轉換為型別化的指標物件,例如,
Foo *的實體可能是這樣編碼的字串:
_108e688_p_Foo
At a basic level, the type checker simply restores some type-safety to extension modules. However, the type checker is also responsible for making sure that wrapped C++ classes are handled correctly---especially when inheritance is used. This is especially important when an extension module makes use of multiple inheritance. For example:
從根本上講,型別檢查器只是將一些型別安全性恢復到擴展模塊,但是,型別檢查器還負責確保正確處理包裝的 C++ 類——尤其是在使用繼承時,當擴展模塊利用多重繼承時,這一點尤其重要,例如:
class Foo {
public:
int x;
};
class Bar {
public:
int y;
};
class FooBar : public Foo, public Bar {
public:
int z;
};
When the class FooBar is organized in memory, it contains the contents of the classes Foo and Bar as well as its own data members. For example:
當在記憶體中組織類
FooBar時,它包含類Foo和Bar的內容以及它自己的資料成員,例如:
FooBar --> | -----------| <-- Foo
| int x |
| ----- |<-- Bar
| int y |
| ------------ |
| int z |
| ------------ |
Because of the way that base class data is stacked together, the casting of a Foobar * to either of the base classes may change the actual value of the pointer. This means that it is generally not safe to represent pointers using a simple integer or a bare void *---type tags are needed to implement correct handling of pointer values (and to make adjustments when needed).
In the wrapper code generated for each language, pointers are handled through the use of special type descriptors and conversion functions. For example, if you look at the wrapper code for Python, you will see code similar to the following (simplified for brevity):
由于將基類資料堆疊在一起的方式,將
Foobar *強制轉換為任一基類都可能會更改指標的實際值,這意味著使用一個簡單的整數或一個簡單的void *——來表示指標通常是不安全的——需要使用型別標記來實作對指標值的正確處理(并在需要時進行調整),在為每種語言生成的包裝器代碼中,通過使用特殊的型別描述符和轉換函式來處理指標,例如,如果查看 Python 的包裝器代碼,你將看到類似于以下代碼(為簡潔起見簡化):
if (!SWIG_IsOK(SWIG_ConvertPtr(obj0, (void **) &arg1, SWIGTYPE_p_Foo, 0))) {
SWIG_exception_fail(SWIG_TypeError, "in method 'GrabVal', expecting type Foo");
}
In this code, SWIGTYPE_p_Foo is the type descriptor that describes Foo *. The type descriptor is actually a pointer to a structure that contains information about the type name to use in the target language, a list of equivalent typenames (via typedef or inheritance), and pointer value handling information (if applicable). The SWIG_ConvertPtr() function is simply a utility function that takes a pointer object in the target language and a type-descriptor object and uses this information to generate a C++ pointer. The SWIG_IsOKmacro checks the return value for errors and SWIG_exception_fail can be called to raise an exception in the target language. However, the exact name and calling conventions of the conversion function depends on the target language (see language specific chapters for details).
The actual type code is in swigrun.swg, and gets inserted near the top of the generated swig wrapper file. The phrase "a type X that can cast into a type Y" means that given a type X, it can be converted into a type Y. In other words, X is a derived class of Y or X is a typedef of Y. The structure to store type information looks like this:
在這段代碼中,
SWIGTYPE_p_Foo是描述Foo *的型別描述符,型別描述符實際上是指向結構體的指標,該結構體包含有關要在目標語言中使用的型別名稱的資訊,等效型別名稱的串列(通過typedef或繼承)以及指標值處理資訊(如果適用),SWIG_ConvertPtr()函式只是一個實用函式,它接受目標語言中的指標物件和型別描述符物件,并使用此資訊生成 C++ 指標,SWIG_IsOK宏檢查錯誤的回傳值,并且可以呼叫SWIG_exception_fail引發目標語言中的例外,但是,轉換函式的確切名稱和呼叫約定取決于目標語言(有關詳細資訊,請參見特定于語言的章節),實際的型別代碼在
swigrun.swg中,并插入到生成的 swig 包裝檔案頂部附近,短語“可以轉換為Y型別的X型別”表示給定X型別,可以將其轉換為Y型別,換句話說,X是Y的派生類,或者X是Y的typedef,存盤型別資訊的結構體如下所示:
/* Structure to store information on one type */
typedef struct swig_type_info {
const char *name; /* mangled name of this type */
const char *str; /* human readable name for this type */
swig_dycast_func dcast; /* dynamic cast function down a hierarchy */
struct swig_cast_info *cast; /* Linked list of types that can cast into this type */
void *clientdata; /* Language specific type data */
} swig_type_info;
/* Structure to store a type and conversion function used for casting */
typedef struct swig_cast_info {
swig_type_info *type; /* pointer to type that is equivalent to this type */
swig_converter_func converter; /* function to cast the void pointers */
struct swig_cast_info *next; /* pointer to next cast in linked list */
struct swig_cast_info *prev; /* pointer to the previous cast */
} swig_cast_info;
Each swig_type_info stores a linked list of types that it is equivalent to. Each entry in this doubly linked list stores a pointer back to another swig_type_info structure, along with a pointer to a conversion function. This conversion function is used to solve the above problem of the FooBar class, correctly returning a pointer to the type we want.
The basic problem we need to solve is verifying and building arguments passed to functions. So going back to the SWIG_ConvertPtr() function example from above, we are expecting a Foo * and need to check if obj0 is in fact a Foo *. From before, SWIGTYPE_p_Foo is just a pointer to the swig_type_infostructure describing Foo *. So we loop through the linked list of swig_cast_info structures attached to SWIGTYPE_p_Foo. If we see that the type of obj0 is in the linked list, we pass the object through the associated conversion function and then return a positive. If we reach the end of the linked list without a match, then obj0 can not be converted to a Foo * and an error is generated.
Another issue needing to be addressed is sharing type information between multiple modules. More explicitly, we need to have ONE swig_type_info for each type. If two modules both use the type, the second module loaded must lookup and use the swig_type_info structure from the module already loaded. Because no dynamic memory is used and the circular dependencies of the casting information, loading the type information is somewhat tricky, and not explained here. A complete description is in the Lib/swiginit.swg file (and near the top of any generated file).
Each module has one swig_module_info structure which looks like this:
每個
swig_type_info都存盤一個等效的型別的鏈表,這個雙向鏈接串列中的每個條目都存盤著一個指向另一個swig_type_info結構體的指標,以及一個指向轉換函式的指標,此轉換函式用于解決FooBar類的上述問題,正確回傳指向所需型別的指標,我們需要解決的基本問題是驗證和構建傳遞給函式的引數,因此,從上面回到
SWIG_ConvertPtr()函式示例,我們期望的是Foo *,并且需要檢查obj0實際上是否為Foo *,從前,SWIGTYPE_p_Foo只是指向描述Foo *的swig_type_info結構體的指標,因此,我們遍歷附加到SWIGTYPE_p_Foo的swig_cast_info結構體的鏈接串列,如果我們看到obj0的型別在鏈表中,則將物件傳遞給關聯的轉換函式,然后回傳一個正數,如果我們到達鏈表的末尾但沒有匹配項,則無法將obj0轉換為Foo *并生成錯誤,需要解決的另一個問題是在多個模塊之間共享型別資訊,更明確地說,我們需要為每種型別使用一個
swig_type_info,如果兩個模塊都使用該型別,則加載的第二個模塊必須從已加載的模塊中查找并使用swig_type_info結構體,因為沒有使用動態記憶體,而且轉換資訊的回圈依賴關系,所以加載型別資訊有些棘手,這里不再贅述,完整的描述在Lib/swiginit.swg檔案中(并且在任何生成的檔案的頂部附近),每個模塊都有一個
swig_module_info結構體,如下所示:
/* Structure used to store module information
* Each module generates one structure like this, and the runtime collects
* all of these structures and stores them in a circularly linked list.*/
typedef struct swig_module_info {
swig_type_info **types; /* Array of pointers to swig_type_info structs in this module */
int size; /* Number of types in this module */
struct swig_module_info *next; /* Pointer to next element in circularly linked list */
swig_type_info **type_initial; /* Array of initially generated type structures */
swig_cast_info **cast_initial; /* Array of initially generated casting structures */
void *clientdata; /* Language specific module data */
} swig_module_info;
Each module stores an array of pointers to swig_type_info structures and the number of types in this module. So when a second module is loaded, it finds the swig_module_info structure for the first module and searches the array of types. If any of its own types are in the first module and have already been loaded, it uses those swig_type_info structures rather than creating new ones. These swig_module_info structures are chained together in a circularly linked list.
每個模塊存盤一個指向
swig_type_info結構體的指標陣列以及該模塊中型別的數量,因此,在加載第二個模塊時,它將為第一個模塊找到swig_module_info結構體,并搜索型別陣列,如果在第一個模塊中有任何自己的型別并且已經被加載,則它使用那些swig_type_info結構體而不是創建新的結構體,這些swig_module_info結構體以回圈鏈接串列的形式鏈接在一起,
11.12.2 使用
This section covers how to use these functions from typemaps. To learn how to call these functions from external files (not the generated _wrap.c file), see the External access to the run-time system section.
When pointers are converted in a typemap, the typemap code often looks similar to this:
本節介紹如何使用型別映射中的這些功能,要了解如何從外部檔案(而不是生成的
_wrap.c檔案)中呼叫這些函式,請參見對運行時系統的外部訪問章節,在型別映射中轉換指標時,型別映射代碼通常看起來類似于以下內容:
%typemap(in) Foo * {
if (!SWIG_IsOK(SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor, 0))) {
SWIG_exception_fail(SWIG_TypeError, "in method '$symname', expecting type Foo");
}
}
The most critical part is the typemap is the use of the $1_descriptor special variable. When placed in a typemap, this is expanded into the SWIGTYPE_* type descriptor object above. As a general rule, you should always use $1_descriptor instead of trying to hard-code the type descriptor name directly.
There is another reason why you should always use the $1_descriptor variable. When this special variable is expanded, SWIG marks the corresponding type as "in use." When type-tables and type information is emitted in the wrapper file, descriptor information is only generated for those datatypes that were actually used in the interface. This greatly reduces the size of the type tables and improves efficiency.
Occasionally, you might need to write a typemap that needs to convert pointers of other types. To handle this, the special variable macro $descriptor(type) covered earlier can be used to generate the SWIG type descriptor name for any C datatype. For example:
最關鍵的部分是型別映射是
$1_descriptor特殊變數的使用,當放置在型別映射中時,它會擴展到上面的SWIGTYPE_*型別描述符物件中,通常,應該始終使用$1_descriptor而不是嘗試直接對型別描述符名稱進行硬編碼,還有另一個原因,為什么你應該始終使用
$1_descriptor變數,擴展此特殊變數后,SWIG 會將相應的型別標記為“使用中”,當在包裝檔案中發出型別表和型別資訊時,僅為介面中實際使用的那些資料型別生成描述符資訊,這大大減小了型別表的大小并提高了效率,有時,你可能需要撰寫一個型別映射,該型別映射需要轉換其他型別的指標,為了解決這個問題,前面介紹的特殊變數宏
$descriptor(type)可用于為任何 C 資料型別生成 SWIG 型別描述符名稱,例如:
%typemap(in) Foo * {
if (!SWIG_IsOK(SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor, 0))) {
Bar *temp;
if (!SWIG_IsOK(SWIG_ConvertPtr($input, (void **) &temp, $descriptor(Bar *), 0))) {
SWIG_exception_fail(SWIG_TypeError, "in method '$symname', expecting type Foo or Bar");
}
$1 = (Foo *)temp;
}
}
The primary use of $descriptor(type) is when writing typemaps for container objects and other complex data structures. There are some restrictions on the argument---namely it must be a fully defined C datatype. It can not be any of the special typemap variables.
In certain cases, SWIG may not generate type-descriptors like you expect. For example, if you are converting pointers in some non-standard way or working with an unusual combination of interface files and modules, you may find that SWIG omits information for a specific type descriptor. To fix this, you may need to use the %types directive. For example:
$descriptor(type)的主要用途是為容器物件和其他復雜資料結構撰寫型別映射時,引數有一些限制——即它必須是完全定義的 C 資料型別,它不能是任何特殊的型別映射變數,在某些情況下,SWIG 可能不會生成你期望的型別描述符,例如,如果你以某種非標準的方式轉換指標或使用介面檔案和模塊的例外組合,則可能會發現 SWIG 忽略了特定型別描述符的資訊,為了解決這個問題,你可能需要使用
%types指令,例如:
%types(int *, short *, long *, float *, double *);
When %types is used, SWIG generates type-descriptor information even if those datatypes never appear elsewhere in the interface file.
Further details about the run-time type checking can be found in the documentation for individual language modules. Reading the source code may also help. The file Lib/swigrun.swg in the SWIG library contains all of the source of the generated code for type-checking. This code is also included in every generated wrapped file so you probably just look at the output of SWIG to get a better sense for how types are managed.
當使用
%types時,SWIG 會生成型別描述符資訊,即使這些資料型別從不出現在介面檔案的其他位置,有關運行時型別檢查的更多詳細資訊,請參見各個語言模塊的檔案,閱讀源代碼也可能會有所幫助,SWIG 庫中的
Lib/swigrun.swg檔案包含用于型別檢查的生成代碼的所有源,該代碼也包含在每個生成的包裝檔案中,因此你可能只需查看 SWIG 的輸出即可更好地了解如何管理型別,
11.13 型別映射與多載
This section does not apply to the statically typed languages like Java and C#, where overloading of the types is handled much like C++ by generating overloaded methods in the target language. In many of the other target languages, SWIG still fully supports C++ overloaded methods and functions. For example, if you have a collection of functions like this:
本章節不適用于 Java 和 C# 等靜態型別的語言,在這些型別中,型別的多載與 C++ 一樣,是通過在目標語言中生成多載的方法來處理的,在許多其他目標語言中,SWIG 仍完全支持 C++ 多載方法和函式,例如,如果你具有以下功能集合:
int foo(int x);
int foo(double x);
int foo(char *s, int y);
You can access the functions in a normal way from the scripting interpreter:
你可以從腳本解釋器以常規方式訪問函式:
# Python
foo(3) # foo(int)
foo(3.5) # foo(double)
foo("hello", 5) # foo(char *, int)
# Tcl
foo 3 # foo(int)
foo 3.5 # foo(double)
foo hello 5 # foo(char *, int)
To implement overloading, SWIG generates a separate wrapper function for each overloaded method. For example, the above functions would produce something roughly like this:
為了實作多載,SWIG 為每個多載方法生成一個單獨的包裝器函式,例如,以上函式將產生大致如下所示的內容:
// wrapper pseudocode
_wrap_foo_0(argc, args[]) { // foo(int)
int arg1;
int result;
...
arg1 = FromInteger(args[0]);
result = foo(arg1);
return ToInteger(result);
}
_wrap_foo_1(argc, args[]) { // foo(double)
double arg1;
int result;
...
arg1 = FromDouble(args[0]);
result = foo(arg1);
return ToInteger(result);
}
_wrap_foo_2(argc, args[]) { // foo(char *, int)
char *arg1;
int arg2;
int result;
...
arg1 = FromString(args[0]);
arg2 = FromInteger(args[1]);
result = foo(arg1, arg2);
return ToInteger(result);
}
Next, a dynamic dispatch function is generated:
接著生成動態調度函式:
_wrap_foo(argc, args[]) {
if (argc == 1) {
if (IsInteger(args[0])) {
return _wrap_foo_0(argc, args);
}
if (IsDouble(args[0])) {
return _wrap_foo_1(argc, args);
}
}
if (argc == 2) {
if (IsString(args[0]) && IsInteger(args[1])) {
return _wrap_foo_2(argc, args);
}
}
error("No matching function!\n");
}
The purpose of the dynamic dispatch function is to select the appropriate C++ function based on argument types---a task that must be performed at runtime in most of SWIG's target languages.
The generation of the dynamic dispatch function is a relatively tricky affair. Not only must input typemaps be taken into account (these typemaps can radically change the types of arguments accepted), but overloaded methods must also be sorted and checked in a very specific order to resolve potential ambiguity. A high-level overview of this ranking process is found in the "SWIG and C++" chapter. What isn't mentioned in that chapter is the mechanism by which it is implemented---as a collection of typemaps.
To support dynamic dispatch, SWIG first defines a general purpose type hierarchy as follows:
動態調度函式的目的是根據引數型別選擇適當的 C++ 函式,這是大多數 SWIG 目標語言都必須在運行時執行的任務,
動態調度函式的生成是一個比較棘手的事情,不僅必須考慮輸入型別映射(這些型別映射可以從根本上改變接受的引數的型別),而且還必須以非常特定的順序對多載方法進行排序和檢查,以解決潛在的歧義,《SWIG 和 C++》一章中提供了有關此排名程序的高級概述,在這一章中沒有提到的是實作它的機制——作為型別映射的集合,
為了支持動態調度,SWIG 首先定義通用型別層次結構,如下所示:
Symbolic Name Precedence Value
------------------------------ ------------------
SWIG_TYPECHECK_POINTER 0
SWIG_TYPECHECK_VOIDPTR 10
SWIG_TYPECHECK_BOOL 15
SWIG_TYPECHECK_UINT8 20
SWIG_TYPECHECK_INT8 25
SWIG_TYPECHECK_UINT16 30
SWIG_TYPECHECK_INT16 35
SWIG_TYPECHECK_UINT32 40
SWIG_TYPECHECK_INT32 45
SWIG_TYPECHECK_UINT64 50
SWIG_TYPECHECK_INT64 55
SWIG_TYPECHECK_UINT128 60
SWIG_TYPECHECK_INT128 65
SWIG_TYPECHECK_INTEGER 70
SWIG_TYPECHECK_FLOAT 80
SWIG_TYPECHECK_DOUBLE 90
SWIG_TYPECHECK_COMPLEX 100
SWIG_TYPECHECK_UNICHAR 110
SWIG_TYPECHECK_UNISTRING 120
SWIG_TYPECHECK_CHAR 130
SWIG_TYPECHECK_STRING 140
SWIG_TYPECHECK_BOOL_ARRAY 1015
SWIG_TYPECHECK_INT8_ARRAY 1025
SWIG_TYPECHECK_INT16_ARRAY 1035
SWIG_TYPECHECK_INT32_ARRAY 1045
SWIG_TYPECHECK_INT64_ARRAY 1055
SWIG_TYPECHECK_INT128_ARRAY 1065
SWIG_TYPECHECK_FLOAT_ARRAY 1080
SWIG_TYPECHECK_DOUBLE_ARRAY 1090
SWIG_TYPECHECK_CHAR_ARRAY 1130
SWIG_TYPECHECK_STRING_ARRAY 1140
(These precedence levels are defined in swig.swg, a library file that's included by all target language modules.)
In this table, the precedence-level determines the order in which types are going to be checked. Low values are always checked before higher values. For example, integers are checked before floats, single values are checked before arrays, and so forth.
Using the above table as a guide, each target language defines a collection of "typecheck" typemaps. The following excerpt from the Python module illustrates this:
(這些優先級在
swig.swg中定義,swig.swg是所有目標語言模塊都包含的庫檔案,)在此表中,優先級確定要檢查的型別的順序,始終先檢查低值,然后再檢查高值,例如,在浮點數之前檢查整數,在陣列之前檢查單個值,依此類推,
使用上表作為指導,每種目標語言都定義了
typecheck型別映射的集合,以下 Python 模塊摘錄說明了這一點:
/* Python type checking rules */
/* Note: %typecheck(X) is a macro for %typemap(typecheck, precedence=X) */
%typecheck(SWIG_TYPECHECK_INTEGER)
int, short, long,
unsigned int, unsigned short, unsigned long,
signed char, unsigned char,
long long, unsigned long long,
const int &, const short &, const long &,
const unsigned int &, const unsigned short &, const unsigned long &,
const long long &, const unsigned long long &,
enum SWIGTYPE,
bool, const bool &
{
$1 = (PyInt_Check($input) || PyLong_Check($input)) ? 1 : 0;
}
%typecheck(SWIG_TYPECHECK_DOUBLE)
float, double,
const float &, const double &
{
$1 = (PyFloat_Check($input) || PyInt_Check($input) || PyLong_Check($input)) ? 1 : 0;
}
%typecheck(SWIG_TYPECHECK_CHAR) char {
$1 = (PyString_Check($input) && (PyString_Size($input) == 1)) ? 1 : 0;
}
%typecheck(SWIG_TYPECHECK_STRING) char * {
$1 = PyString_Check($input) ? 1 : 0;
}
%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER, noblock=1) SWIGTYPE * {
void *vptr = 0;
int res = SWIG_ConvertPtr($input, &vptr, $1_descriptor, 0);
$1 = SWIG_IsOK(res) ? 1 : 0;
}
%typecheck(SWIG_TYPECHECK_POINTER) PyObject * {
$1 = ($input != 0);
}
It might take a bit of contemplation, but this code has merely organized all of the basic C++ types, provided some simple type-checking code, and assigned each type a precedence value.
Finally, to generate the dynamic dispatch function, SWIG uses the following algorithm:
- Overloaded methods are first sorted by the number of required arguments.
- Methods with the same number of arguments are then sorted by precedence values of argument types.
- Typecheck typemaps are then emitted to produce a dispatch function that checks arguments in the correct order.
If you haven't written any typemaps of your own, it is unnecessary to worry about the typechecking rules. However, if you have written new input typemaps, you might have to supply a typechecking rule as well. An easy way to do this is to simply copy one of the existing typechecking rules. Here is an example,
這可能需要一些考慮,但是此代碼僅組織了所有基本 C++ 型別,提供了一些簡單的型別檢查代碼,并為每種型別分配了優先級值,
最后,為了生成動態調度功能,SWIG 使用以下演算法:
- 多載的方法首先按所需引數的數量排序,
- 然后,將具有相同數量引數的方法按引數型別的優先級值排序,
- 然后發出
typecheck型別映射,以產生一個調度函式,該函式以正確的順序檢查引數,如果你尚未撰寫任何型別映射,則不必擔心型別檢查規則,但是,如果你撰寫了新的輸入型別映射,則可能還必須提供型別檢查規則,一種簡單的方法是簡單地復制現有的型別檢查規則之一,這是一個例子
// Typemap for a C++ string
%typemap(in) std::string {
if (PyString_Check($input)) {
$1 = std::string(PyString_AsString($input));
} else {
SWIG_exception(SWIG_TypeError, "string expected");
}
}
// Copy the typecheck code for "char *".
%typemap(typecheck) std::string = char *;
The bottom line: If you are writing new typemaps and you are using overloaded methods, you will probably have to write new typecheck code or copy and modify existing typecheck code.
If you write a typecheck typemap and omit the precedence level, for example commenting it out as shown below:
底線:如果你正在撰寫新的型別映射,并且使用的是多載方法,則可能必須撰寫新的型別檢查代碼或復制和修改現有的型別檢查代碼,
如果撰寫型別檢查型別映射并忽略優先級,例如將其注釋掉,如下所示:
%typemap(typecheck /*, precedence=SWIG_TYPECHECK_INTEGER*/) int {
$1 = PyInt_Check($input) ? 1 : 0;
}
then the type is given a precedence higher than any other known precedence level and a warning is issued:
然后為該型別賦予比其他任何已知優先級高的優先級,并發出警告:
example.i:18: Warning 467: Overloaded method foo(int) not supported (incomplete type checking rule - no precedence level in typecheck typemap for 'int').
Notes:
- Typecheck typemaps are not used for non-overloaded methods. Because of this, it is still always necessary to check types in any "in" typemaps.
- The dynamic dispatch process is only meant to be a heuristic. There are many corner cases where SWIG simply can't disambiguate types to the same degree as C++. The only way to resolve this ambiguity is to use the %rename directive to rename one of the overloaded methods (effectively eliminating overloading).
- Typechecking may be partial. For example, if working with arrays, the typecheck code might simply check the type of the first array element and use that to dispatch to the correct function. Subsequent "in" typemaps would then perform more extensive type-checking.
- Make sure you read the section on overloading in the "SWIG and C++" chapter.
注意:
typecheck型別映射不適用于非多載方法,因此,仍然始終需要檢查任何in型別映射中的型別,- 動態調度程序僅是一種啟發式方法,在許多特殊情況下,SWIG 不能完全消除型別與 C++ 相同的歧義,解決此歧義的唯一方法是使用
%rename指令重命名其中一種多載方法(有效消除多載),- 型別檢查可能是部分的,例如,如果使用陣列,則型別檢查代碼可以簡單地檢查第一個陣列元素的型別,然后使用它來分派給正確的函式,隨后的
in型別映射將執行更廣泛的型別檢查,- 確保你已閱讀《SWIG 和 C++》一章中有關多載的部分,
11.14 %apply 和 %clear 詳情
In order to implement certain kinds of program behavior, it is sometimes necessary to write sets of typemaps. For example, to support output arguments, one often writes a set of typemaps like this:
為了實作某些型別的程式行為,有時有必要撰寫型別映射集,例如,為了支持輸出引數,通常會撰寫這樣的一組型別映射:
%typemap(in, numinputs=0) int *OUTPUT (int temp) {
$1 = &temp;
}
%typemap(argout) int *OUTPUT {
// return value somehow
}
To make it easier to apply the typemap to different argument types and names, the %apply directive performs a copy of all typemaps from one type to another. For example, if you specify this,
為了更容易地將型別映射應用于不同的引數型別和名稱,
%apply指令將所有型別映射從一種型別復制到另一種型別,例如,如果你指定此選項,
%apply int *OUTPUT { int *retvalue, int32 *output };
then all of the int *OUTPUT typemaps are copied to int *retvalue and int32 *output.
However, there is a subtle aspect of %apply that needs more description. Namely, %apply does not overwrite a typemap rule if it is already defined for the target datatype. This behavior allows you to do two things:
- You can specialize parts of a complex typemap rule by first defining a few typemaps and then using
%applyto incorporate the remaining pieces. - Sets of different typemaps can be applied to the same datatype using repeated
%applydirectives.
For example:
然后將所有
int *OUTPUT型別映射復制到int *retvalue和int32 * output,但是,
%apply有一個細微的方面需要更多描述,就是說,如果此行為使你可以做兩件事:
- 你可以通過首先定義一些型別映射,然后使用
%apply來合并其余部分來特化復雜型別映射規則的各個部分,- 可以使用重復的
%apply指令將不同型別映射的集合應用于相同的資料型別,例如:
%typemap(in) int *INPUT (int temp) {
temp = ... get value from $input ...;
$1 = &temp;
}
%typemap(check) int *POSITIVE {
if (*$1 <= 0) {
SWIG_exception(SWIG_ValueError, "Expected a positive number!\n");
return NULL;
}
}
...
%apply int *INPUT { int *invalue };
%apply int *POSITIVE { int *invalue };
Since %apply does not overwrite or replace any existing rules, the only way to reset behavior is to use the %clear directive. %clear removes all typemap rules defined for a specific datatype. For example:
由于
%apply不會覆寫或替換任何現有規則,因此重置行為的唯一方法是使用%clear偽指令,%clear洗掉為特定資料型別定義的所有型別映射規則,例如:
%clear int *invalue;
11.15 在型別映射間傳遞資料
It is also important to note that the primary use of local variables is to create stack-allocated objects for temporary use inside a wrapper function (this is faster and less-prone to error than allocating data on the heap). In general, the variables are not intended to pass information between different types of typemaps. However, this can be done if you realize that local names have the argument number appended to them. For example, you could do this:
同樣重要的是要注意,區域變數的主要用途是創建包裝分配的物件,以便在包裝器函式內部臨時使用(與在堆上分配資料相比,此方法更快且更不容易出錯),通常,這些變數無意在不同型別的型別映射之間傳遞資訊,但是,如果你意識到區域名稱后面附加了引數編號,則可以這樣做,例如,你可以這樣做:
%typemap(in) int *(int temp) {
temp = (int) PyInt_AsLong($input);
$1 = &temp;
}
%typemap(argout) int * {
PyObject *o = PyInt_FromLong(temp$argnum);
...
}
In this case, the $argnum variable is expanded into the argument number. Therefore, the code will reference the appropriate local such as temp1 and temp2. It should be noted that there are plenty of opportunities to break the universe here and that accessing locals in this manner should probably be avoided. At the very least, you should make sure that the typemaps sharing information have exactly the same types and names.
在這種情況下,
$argnum變數將擴展為引數編號,因此,代碼將參考適當的區域變數,例如temp1和temp2,應當指出,這里有很多打破宇宙的機會,應該避免以這種方式訪問區域變數,至少,你應該確保共享資訊的型別映射具有完全相同的型別和名稱,
11.16 C++ this 指標
All the rules discussed for typemaps apply to C++ as well as C. However in addition C++ passes an extra parameter into every non-static class method -- the this pointer. Occasionally it can be useful to apply a typemap to this pointer (for example to check and make sure this is non-null before deferencing). Actually, C also has an the equivalent of the this pointer which is used when accessing variables in a C struct.
In order to customise the this pointer handling, target a variable named self in your typemaps. self is the name SWIG uses to refer to the extra parameter in wrapped functions.
For example, if wrapping for Java generation:
討論型別映射的所有規則都適用于 C++ 和 C,但是,此外,C++ 向每個非靜態類方法(
this指標)傳遞了一個額外的引數,有時候,將型別映射應用于此指標可能會很有用(例如,檢查并確保在遞延前確保this為非空),實際上,C 還具有等效于this指標的指標,該指標在訪問 C 結構體中的變數時使用,為了自定義
this指標處理,在型別映射中定位一個名為self的變數,self是 SWIG 在包裝器函式中用來參考附加引數的名稱,例如,如果包裝為生成 Java:
%typemap(check) SWIGTYPE *self %{
if (!$1) {
SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException,
"invalid native object; delete() likely already called");
return $null;
}
%}
In the above case, the $1 variable is expanded into the argument name that SWIG is using as the thispointer. SWIG will then insert the check code before the actual C++ class method is called, and will raise an exception rather than crash the Java virtual machine. The generated code will look something like:
在上述情況下,將
$1變數擴展為 SWIG 用作this指標的引數名稱,然后,SWIG 將在呼叫實際的 C++ 類方法之前插入檢查代碼,并且將引發例外而不是使 Java 虛擬機崩潰,生成的代碼如下所示:
if (!arg1) {
SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException,
"invalid native object; delete() likely already called");
return ;
}
(arg1)->wrappedFunction(...);
Note that if you have a parameter named self then it will also match the typemap. One work around is to create an interface file that wraps the method, but gives the argument a name other than self.
請注意,如果你有一個名為
self的引數,則它也將與型別映射匹配,一種解決方法是創建一個包裝該方法的介面檔案,但為自變數指定一個不同于self的名稱,
11.17 到哪去找更多的資訊?
The best place to find out more information about writing typemaps is to look in the SWIG library. Most language modules define all of their default behavior using typemaps. These are found in files such aspython.swg, perl5.swg, tcl8.swg and so forth. The typemaps.i file in the library also contains numerous examples. You should look at these files to get a feel for how to define typemaps of your own. Some of the language modules support additional typemaps and further information is available in the individual chapters for each target language. There you may also find more hands-on practical examples.
要尋找有關撰寫型別映射的更多資訊,最佳的地點是在 SWIG 庫中,大多數語言模塊都使用型別映射定義所有默認行為,這些可以在諸如
python.swg、perl5.swg、tcl8.swg等檔案中找到,庫中的typemaps.i檔案也包含許多示例,你應該查看這些檔案,以了解如何定義自己的型別映射,一些語言模塊支持其他型別映射,并且在每種章節的每種目標語言中都提供了更多資訊,在這里你還可以找到更多動手的實際示例,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/17337.html
標籤:C++
