目錄
- 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.4.1
- 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.5.1
- 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 引言
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 預設情況下的行為認識模糊,那麼你可能想重新閱讀前面的章節。
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 代碼的短代碼集合處理——你隻需閱讀自己喜歡的語言的擴充文檔以了解其工作原理(一個留給讀者的練習)。
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:
乍看之下,這段代碼看起來有些混亂。但是,實際上并沒有太多。第一個類型映射(類型映射)用于将值從目智語言轉換為 C。第二個類型映射(
in
類型映射)用于向另一個方向轉換。每個類型映射的内容都是一小段代碼,直接插入 SWIG 生成的包裝器函數中。該代碼通常是 C 或 C++ 代碼,它們将生成到 C/C++ 包裝器函數中。請注意,并非總是如此,因為某些目智語言子產品允許類型映射中的目智語言代碼生成到目智語言特定的檔案中。在此代碼中,将擴充許多帶有
out
字首的特殊變量。這些實際上隻是 C/C++ 變量的占位符,這些變量是在建立包裝器函數的過程中生成的。在這種情況下,
$
是指需要轉換為 C/C++ 的輸入對象,而
$input
是指将由包裝器函數傳回的對象。
$result
指的是一個 C/C++ 變量,其類型與類型映射聲明中指定的類型相同(本例中為
$1
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.
在此代碼中,你可以看到如何将類型映射代碼插入到函數中。你還可以看到特殊的 $
變量是如何擴充的,以比對包裝器函數中的某些變量名稱。這實際上就是類型映射背後的全部思想,它們隻是讓你将任意代碼插入生成的包裝器函數的不同部分。由于可以插入任意代碼,是以可以完全改變值轉換的方式。
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:
在這種情況下,即使類型名并不總是與文本比對,也仍然将類型映射應用于适當的參數。這種跟蹤類型的能力是 SWIG 的重要組成部分——實際上,所有目智語言子產品都隻能為基本類型定義一組類型映射。但是,從來沒有必要為
int
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
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
的類型映射。你不必做任何事情。
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.
每個類型映射的詳細内容很快會提到。同樣,某些語言子產品可能會定義其他類型映射以擴充此清單。例如,Java 子產品定義了各種類型映射來控制 Java 綁定的其他方面。請查閱特定于語言的文檔以擷取更多詳細資訊。
- 建立常數值。(
或
consttab
constcode
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)
相關聯的屬性。是以,為此目的要使用完全不同的 SWIG 定制機制(
Foo *
%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);
}
%}
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 術語如下:也可以将 SWIG 視為具有基于
- 橫切關注點:橫切關注點是類型映射所實作功能的子產品化,主要是将目智語言和 C/C++ 之間的類型進行編組。
- 通知:類型映射主體包含在需要編組時執行的代碼。
- 切入點:切入點是包裝器代碼中生成類型映射代碼的位置。
- 切面:切面是切入點和通知的組合,是以每個類型映射都是一個切面。
的第二組切面。諸如
%feature
之類的功能也是橫切關注點,因為它們封裝了可用于向任何函數添加日志記錄或異常處理的代碼。
%exception
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++ 類型系統緊密相關,是以後續章節假定你對值、指針、引用、數組、類型限定符(例如
)、結構體、命名空間、模闆和 C/C++ 中的記憶體管理相當熟悉。如果不是這樣,建議你先閱讀 Kernighan 和 Ritchie 撰寫的《The C Programming Language》或 Stroustrup 撰寫的《The C++ Programming Language》。
const
This section describes the behavior of the
%typemap
directive itself.
本節描述了 %typemap
指令本身的行為。
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"
是類型映射将比對的 C++ 類型模式的清單。此清單的一般形式如下:
typelist
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.
乍一看,這并不是最易讀的文法。但是,各個部分的目的将變得清楚。
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.
};
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
的模式遵循與
%apply
相同的規則。例如:
%typemap
%apply int *output { Integer *output }; // Typemap with name
%apply (char *buf, int len) { (char *buffer, int size) }; // Multiple arguments
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
)将使該類型不可用。
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
應當注意,為使作用域有效,SWIG 必須知道
Foo::string
是在特定名稱空間内定義的類型名。在此示例中,這是使用正向類聲明
string
完成的。
class string
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++ 資料類型與類型映射相關聯。實際中,可以通過使用調試選項來觀察比對規則。
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
TYPE
NAME
-
only.TYPE
- If
is a C++ template of typeTYPE
, whereT<TPARMS>
are the template parameters, the type is stripped of the template parameters and the following checks are then made:TPARMS
-
T
NAME
-
T
-
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 *
TYPE
is an array. The following transformation is made:
- Replace all dimensions to
and look for a generic array typemap.[ANY]
To illustrate, suppose that you had a function like this:
使用類型和名稱(通常是參數名稱)來比對類型映射。對于給定的配對,将應用以下規則來查找比對項。第一個找到的類型映射将被使用。
TYPE NAME
- 與
TYPE
完全比對的類型映射。
NAME
- 僅與
TYPE
- 如果
是
TYPE
類型的 C++ 模闆,其中
T<TPARMS>
是模闆參數,則将類型的模闆參數剝離,然後進行以下檢查:
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 引入了一次删除一個限定符。先前的版本一次就消除了所有限定符。
typedef
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
的“還原”過程,也就是說,SWIG 不會搜尋每個可能的
typedef
。給定聲明中的類型,它隻會還原類型,而不會在尋找
typedef
時建立它。例如,給定類型為
typedef
,由于
Struct
已被完全還原,是以以下類型映射将不會用于
Struct
參數:
aStruct
struct Struct {...};
typedef Struct StructTypedef;
%typemap(in) StructTypedef {
...
}
void go(Struct aStruct);
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 *
。更準确地說,這些規則基于 C++ 類模闆偏特化比對規則,這些比對規則由 C++ 編譯器在尋找合适的模闆偏特化時使用。這意味着從可用的最特定的通用類型映射類型集合中選擇一個比對項。例如,當尋找與
SWIGTYPE &
的比對項時,規則将優先比對
int const *
(如果有的話),然後再比對
SWIGTYPE const *
,再比對
SWIGTYPE *
大多數 SWIG 語言子產品都使用類型映射來定義 C 基本類型的預設行為。這是非常簡單的。例如,按值或常引用編組的原始類型的一組類型映射如下所示:
SWIGTYPE
%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);
Vector
type will usually just get matched against
SWIGTYPE
. The default implementation of
SWIGTYPE
is 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
的所有類型映射,則 SWIG 不會包裝任何未知的資料類型(這可能對調試很有用)。或者,你可以修改
SWIGTYPE
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++ 類模闆偏特化比對規則。
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.
多參數類型映射在比對方式上也有更多限制。目前,第一個參數遵循上一節中描述的比對規則,但是所有後續參數必須完全比對。
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++ 模闆的人來說,比較類型映射比對規則和模闆類型推導是很有趣的。首先考慮的兩個方面是預設類型映射及其與模闆偏特化的相似性,其次是非預設類型映射及其與模闆完全化的相似性。
對于預設(
)類型映射,規則受 C++ 類模闆偏特化的啟發。例如,給定
SWIGTYPE
的偏特化:
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 &
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
處理,但至少應該清楚的是,類型映射比對規則類似于特化模闆處理的規則。
There are two useful debug command line options available for debugging typemaps,
-debug-tmsearch
-debug-tmused
-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 name
-
Using: %typemap(method) type name = type2 name2
-
Using: %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 name
Using: %typemap(method) type name = type2 name2
Using: %apply type2 name2 { type name }
選項調用 SWIG 以顯示預處理後的輸出,并搜尋所使用的特定類型映射,則将找到完整的類型映射内容(以下示例顯示在 Python 中):
-E
%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-tmsearch
option. 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
or%typemap
is displayed, for example, the "check" and "in" typemaps.%apply
- The typemap modifiers are not shown, eg the
modifier in the "arginit" typemap.noblock=1
- The exact
statement might look different to what is in the actual code. For example, the%apply
is not shown as it is not relevant here. Also the types may be displayed slightly differently -const char* another_value
and notchar const *
const char*
可以注意到以下有關顯示内容的觀察結果(同樣适用):
-debug-tmsearch
- 顯示了相關的類型映射,但是對于複制類型映射,将顯示适當的
%typemap
,例如,
%apply
check
類型映射。
in
- 類型映射修飾符未顯示,例如,
類型映射中的
arginit
修飾符。
noblock = 1
- 确切的
語句可能看起來與實際代碼不同。例如,未顯示
%apply
,因為此處不相關。同樣,類型的顯示可能略有不同——
const char* another_value
而不是
char const*
const char*
This section describes rules by which typemap code is inserted into the generated wrapper code.
本節描述将類型映射代碼插入到生成的包裝器代碼中的規則。
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 預處理程式傳遞的類型映射代碼。
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 *
對象,将 Perl 字元串資料複制到其中,然後将指針傳遞給該對象。為此,隻需使用如下額外參數指定類型映射:
std::string
%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
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
....
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 |
---|---|
| A C local variable corresponding to type n in the typemap pattern. |
| Argument number. Only available in typemaps related to argument conversion |
| Argument name |
| Real C datatype of type n. |
| ltype of type n |
| Mangled form of type n. For example |
| Type descriptor structure for type n. For example . This is primarily used when interacting with the run-time type checker (described later). |
| Real C datatype of type n with one pointer removed. |
| ltype of type n with one pointer removed. |
| Mangled form of type n with one pointer removed. |
| Type descriptor structure for type n with one pointer removed. |
| Real C datatype of type n with one pointer added. |
| ltype of type n with one pointer added. |
| Mangled form of type n with one pointer added. |
| Type descriptor structure for type n with one pointer added. |
| 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 {
}
$1
refers to
int *INPUT
. If you have a typemap like this,
那麼指向
$1
。如果你有如下類型映射,
int *INPUT
%typemap(in) (int argc, char *argv[]) {
...
}
$1
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
與類型和名稱相關的替換總是填充比對的實際代碼中的值。當一個類型映射可能比對多個 C 資料類型時很有用。例如:
char *argv[]
%typemap(in) int, short, long {
$1 = ($1_ltype) PyInt_AsLong($input);
}
$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
$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
的 C/C++ 資料類型始終是
$2
ltype
隻是可以合法出現在 C 指派操作左側的類型。以下是一些類型和
ltype
的示例:
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
$*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 */
...
}
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 解析或編譯階段完成的。以下特殊變量宏可在所有語言子產品中使用。
$descriptor(type)
$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.
這個宏擴充為中指定的任何 C/C++ 類型的類型描述符結構。它的行為類似于上述的
type
特殊變量,不同之處在于要擴充的類型是從宏參數中擷取的,而不是從類型映射類型中推斷出來的。例如,
$1_descriptor
将擴充為
$descriptor(std::vector<int> *)
。該宏主要用于腳本化目智語言,稍後在運作時類型檢查器用法章節中進行示範。
SWIGTYPE_p_std__vectorT_int_t
$typemap(method, typepattern)
$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
}
%}
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 開始,類型映射屬性還将擴充特殊變量和特殊變量宏。
用法示例顯示
屬性(特定于 C#)以及主要的類型映射主體中的擴充:
out
%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"
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"
The set of typemaps recognized by a language module may vary. However, the following typemap methods are nearly universal:
語言子產品識别的類型映射集可能有所不同。但是,以下類型映射方法幾乎是通用的:
in
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.
此時,隻能轉換零個或一個參數。當設定為 0 時,該參數将被有效忽略,并且無法從目智語言中提供。進行 C/C++ 調用時,仍然需要該參數,并且上面的類型映射顯示所使用的值是從局部聲明的名為
numinputs
的變量中擷取的。通常不指定
temp
,是以預設值為
numinputs
1
,即從目智語言到 C/C++ 調用使用時,參數數量是一對一的映射。多參數類型映射提供了類似的概念,其中可以為多個相鄰的 C更改從目智語言映射到 C/C++ 的參數數量 / C++ 參數。
注意相容性:指定
與舊的
numinputs = 0
類型映射相同。
ignore
typecheck
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
,否則設定為 如果你定義新的
1
類型映射,并且你的程式使用重載方法,則還應該定義
in
類型映射的集合。有關此問題的更多詳細資訊,請參見類型映射與重載章節。
typecheck
out
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
arginit
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;
}
default
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#)實際上會忽略此類型映射所指定的值,因為必須提供所有參數。
将預設類型映射應用于參數後,後面的所有參數都必須具有預設值。有關預設參數包裝的更多資訊,請參見預設與可選參數章節。
check
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.");
}
}
argout
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:
類型映射用于從參數傳回值。這最常用于為需要傳回多個值的 C/C++ 函數編寫包裝器。
argout
類型映射幾乎總是與
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
...
}
可以使用下列特殊變量。
$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
freearg
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
newfree
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
章節。
ret
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
,但是兩個函數都将使用 SWIG 提供的
MakeString2
的預設
char *
類型映射。上面的代碼将確定在所有目智語言中釋放适當的記憶體,因為不需要提供自定義的
out
out
類型映射(涉及目智語言特定的代碼)。
這種方法是使用
類型映射和
newfree
的一種替代方法,因為不需要列出所有需要記憶體清理的功能,它完全是在類型上完成的。
%newobject
memberin
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 已經為數組、字元串和其他對象提供了預設實作。
varin
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++ 全局變量。這是特定于實作的。
varout
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++ 對象轉換為目智語言中的對象。這是特定于實作的。
throws
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
類型映射。它提供了一種預設機制來處理聲明了将要抛出的異常的 C++ 方法。此類型映射的目的是将 C++ 異常轉換為目智語言中的錯誤或異常。它與其他類型映射略有不同,因為它基于異常類型而不是參數或變量的類型。例如:
throw
%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
處理異常章節。
This section contains a few examples. Consult language module documentation for more examples.
本節包含一些示例。有關更多示例,請查閱語言子產品的文檔。
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:
類型映射用于從已經從目智語言轉換為 C 的資料中設定結構體成員。在這種情況下,
memberin
$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
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++ 程式來防止潛在的分段錯誤或其他運作時問題。
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);"
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:
當按值傳回對象時,顯示的類型映射是 C# 的預設類型映射。從 C# 調用
out
時,輸出如下:
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:
請注意,正在建立三個對象以及一個配置設定。如果唯一調用構造函數的方法是方法,那不是很好嗎?由于該方法按值傳回,是以要求很多,而 SWIG 預設生成的代碼使編譯器無法使用傳回值優化(RVO)。但是,這是
XX::create()
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,
$1
is 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()
傳回的值進行複制構造函數調用。使用實作 RVO 的現代編譯器,實際上并不會完成複制,實際上,該對象根本不會在
XX::create()
中的堆棧上建立,而隻是在堆上直接建立。首先,将類型映射中的
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
替換。
其次,如果類型映射多次使用
,則将多次調用包裝器函數。顯然,這不是很理想。實際上,SWIG 會嘗試檢測到這一點,并将發出類似以下的警告:
$1
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
在某些注釋掉的代碼中時。
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
$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
%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()
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
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
是函數傳回的實際 C 資料類型。
result
$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++ 參數。需要編寫一種變通方法,例如輔助函數,将參數重新排序以使其連續。
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 都會生成警告消息。請參閱釋出警告章節中的資訊。
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 = 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
等。SWIG 為需要該類型映射代碼的每個參數複制代碼,進而很容易導緻所生成代碼中的代碼膨脹。為了消除這種情況,請定義一個包含通用編組代碼的片段:
directorout
%fragment("AsMyClass", "header") {
MyClass *AsMyClass(PyObject *obj) {
MyClass *value = 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
typemaps above and wrapping the method:MyClass *
并且它們遵循預處理分隔符章節中提到的正常預處理規則。以下是使用片段的一些規則和準則:
- 一個片段僅被添加到包裝代碼一次。當使用上面的
類型映射并包裝方法時:
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
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:
僅使用第一個定義。這樣,你可以通過在庫之前定義片段來覆寫 SWIG 庫中的預設片段。請注意,此行為與類型映射相反,後者以最後定義或應用的類型映射為準。片段遵循先進先出的約定,因為它們是全局的,而類型映射則是局部的。
%include
- 片段名稱不能包含逗号。
- 一個片段可以使用一個或多個其他片段,例如:
%fragment("<limits.h>", "header") {
%#include <limits.h>
}
%fragment("AsMyClass", "header", fragment="<limits.h>") {
MyClass *AsMyClass(PyObject *obj) {
MyClass *value = 0;
... some marshalling code ...
if (ival < CHAR_MIN /*defined in <limits.h>*/) {
...
} 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 類型映射庫的某些部分中使用的某些功能強大但棘手的宏和片段用法,否則大多數讀者可能會希望跳過接下來的兩節有關進階片段用法的小節。
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>} ...
}
};
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 = in_method_##Type(obj);
}
%enddef
%my_typemaps(float);
%my_typemaps(long);
then the proper
"incode"{float}
"incode"{long}
fragment will be used, and the
in_method_float
in_method_long
methods will be called whenever the
float
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
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 代碼需要提供它。
類型系統要求:
- 存儲繼承和類型等效資訊,并能夠正确地重新建立類型指針。
- 在子產品之間共享類型資訊。
- 子產品可以以任何順序加載,而不管實際的類型依賴性如何。
- 避免一般使用動态配置設定的記憶體和庫/系統調用。
- 提供合理快速的實施,以最小化所有語言子產品的查找時間。
- 自定義,特定于語言的資訊可以附加到類型上。
- 可以從類型系統中解除安裝子產品。
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
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_IsOK
macro 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
函數隻是一個實用函數,它接受目智語言中的指針對象和類型描述符對象,并使用此資訊生成 C++ 指針。
SWIG_ConvertPtr()
宏檢查錯誤的傳回值,并且可以調用
SWIG_IsOK
SWIG_exception_fail
引發目智語言中的異常。但是,轉換函數的确切名稱和調用約定取決于目智語言(有關詳細資訊,請參見特定于語言的章節)。
實際的類型代碼在
中,并插入到生成的 swig 包裝檔案頂部附近。短語“可以轉換為
swigrun.swg
類型的
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_info
structure 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
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 會将相應的類型标記為“使用中”。當在包裝檔案中發出類型表和類型資訊時,僅為接口中實際使用的那些資料類型生成描述符資訊。這大大減小了類型表的大小并提高了效率。
有時,你可能需要編寫一個類型映射,該類型映射需要轉換其他類型的指針。為了解決這個問題,前面介紹的特殊變量宏
可用于為任何 C 資料類型生成 SWIG 類型描述符名稱。例如:
$descriptor(type)
%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 庫中的
檔案包含用于類型檢查的生成代碼的所有源。該代碼也包含在每個生成的包裝檔案中,是以你可能隻需檢視 SWIG 的輸出即可更好地了解如何管理類型。
Lib/swigrun.swg
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
是所有目智語言子產品都包含的庫檔案。)
在此表中,優先級确定要檢查的類型的順序。始終先檢查低值,然後再檢查高值。例如,在浮點數之前檢查整數,在數組之前檢查單個值,依此類推。
使用上表作為指導,每種目智語言都定義了
類型映射的集合。以下 Python 子產品摘錄說明了這一點:
typecheck
/* 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++》一章中有關重載的部分。
%apply
%clear
%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
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
to incorporate the remaining pieces.%apply
- Sets of different typemaps can be applied to the same datatype using repeated
directives.%apply
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;
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
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
this
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
為非空)。實際上,C 還具有等效于
this
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
this
pointer. 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:
在上述情況下,将變量擴充為 SWIG 用作
$1
指針的參數名稱。然後,SWIG 将在調用實際的 C++ 類方法之前插入檢查代碼,并且将引發異常而不是使 Java 虛拟機崩潰。生成的代碼如下所示:
this
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
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 as
python.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
★ 持續學習 ★ 堅持創作 ★