SWIG 3 中文手冊——11. 類型映射

目錄

11 類型映射

11.1 引言

Chances are, you are reading this chapter for one of two reasons; you either want to customize SWIG's behavior or you overheard someone mumbling some incomprehensible drivel about "typemaps" and you asked yourself "typemaps, what are those?" That said, let's start with a short disclaimer that "typemaps" are an advanced customization feature that provide direct access to SWIG's low-level code generator. Not only that, they are an integral part of the SWIG C++ type system (a non-trivial topic of its own). Typemaps are generally not a required part of using SWIG. Therefore, you might want to re-read the earlier chapters if you have found your way to this chapter with only a vague idea of what SWIG already does by default.html

你正在閱讀本章的緣由可能有兩個:你想自定義 SWIG 的行爲,或是無心中聽到有人抱怨「typemaps」一詞,並問本身「typemaps 是什麼?」。那麼,讓咱們從一個簡短的免責聲明開始,即「typemaps」是一種高級定製功能,能夠直接訪問 SWIG 的低級代碼生成器。不只如此,它們仍是 SWIG C++ 類型系統(SWIG 自身的重要內容)的組成部分。一般,不是使用 SWIG 的必需部分。所以,若是你閱讀本章時對 SWIG 默認狀況下的行爲認識模糊,那麼你可能想從新閱讀前面的章節。java

11.1.1 類型轉換

One of the most important problems in wrapper code generation is the conversion or marshalling of datatypes between programming languages. Specifically, for every C/C++ declaration, SWIG must somehow generate wrapper code that allows values to be passed back and forth between languages. Since every programming language represents data differently, this is not a simple of matter of simply linking code together with the C linker. Instead, SWIG has to know something about how data is represented in each language and how it can be manipulated.python

To illustrate, suppose you had a simple C function like this:ios

包裝器代碼生成中最重要的問題之一是編程語言之間數據類型的轉換或編組。具體來講,對於每一個 C/C++ 聲明,SWIG 必須以某種方式生成包裝器代碼,該包裝器代碼容許在語言之間來回傳遞值。因爲每種編程語言表示數據的方式都不相同,所以簡單地將代碼與 C 連接器連接在一塊兒並非一件容易的事。相反,SWIG 必須瞭解每種語言如何表示數據,以及如何對其進行操縱的知識。c++

爲了說明這一點,假設你有一個簡單的 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:ruby

第一個函數用於將輸入參數從 Python 整數對象轉換爲 C 中的 long。第二個函數用於將值從 C 轉換回 Python 整數對象。

在包裝器函數中,你可能會看到這些函數的用法以下:

PyObject *wrap_factorial(PyObject *self, PyObject *args) {
  int       arg1;
  int       result;
  PyObject *obj1;
  PyObject *resultobj;

  if (!PyArg_ParseTuple("O:factorial", &obj1)) return NULL;
  arg1 = PyInt_AsLong(obj1);
  result = factorial(arg1);
  resultobj = PyInt_FromLong(result);
  return resultobj;
}

Every target language supported by SWIG has functions that work in a similar manner. For example, in Perl, the following functions are used:

SWIG 支持的每種目標語言都具備以相似方式工做的函數。例如,在 Perl 中,使用如下函數:

IV SvIV(SV *sv);                     /* Perl --> C */
void sv_setiv(SV *sv, IV val);       /* C --> Perl */

In Tcl:

在 Tcl 中:

int Tcl_GetLongFromObj(Tcl_Interp *interp, Tcl_Obj *obj, long *value);
Tcl_Obj *Tcl_NewIntObj(long value);

The precise details are not so important. What is important is that all of the underlying type conversion is handled by collections of utility functions and short bits of C code like this--you simply have to read the extension documentation for your favorite language to know how it works (an exercise left to the reader).

確切的細節不是那麼重要。重要的是,全部底層類型轉換都由實用程序函數和相似這樣的 C 代碼的短代碼集合處理——你只需閱讀本身喜歡的語言的擴展文檔以瞭解其工做原理(一個留給讀者的練習)。

11.1.2 類型映射

Since type handling is so central to wrapper code generation, SWIG allows it to be completely defined (or redefined) by the user. To do this, a special %typemap directive is used. For example:

因爲類型處理對於包裝器代碼生成很是重要,所以 SWIG 容許用戶徹底自定義(或從新定義)它。爲此,使用特殊的 %typemap 指令。例如:

/* Convert from Python --> C */
%typemap(in) int {
  $1 = PyInt_AsLong($input);
}

/* Convert from C --> Python */
%typemap(out) int {
  $result = PyInt_FromLong($1);
}

At first glance, this code will look a little confusing. However, there is really not much to it. The first typemap (the "in" typemap) is used to convert a value from the target language to C. The second typemap (the "out" typemap) is used to convert in the other direction. The content of each typemap is a small fragment of code that is inserted directly into the SWIG generated wrapper functions. The code is usually C or C++ code which will be generated into the C/C++ wrapper functions. Note that this isn't always the case as some target language modules allow target language code within the typemaps which gets generated into target language specific files. Within this code, a number of special variables prefixed with a $ are expanded. These are really just placeholders for C/C++ variables that are generated in the course of creating the wrapper function. In this case, $input refers to an input object that needs to be converted to C/C++ and $result refers to an object that is going to be returned by a wrapper function. $1 refers to a C/C++ variable that has the same type as specified in the typemap declaration (an int in this example).

A short example might make this a little more clear. If you were wrapping a function like this:

乍看之下,這段代碼看起來有些混亂。可是,實際上並無太多。第一個類型映射(in 類型映射)用於將值從目標語言轉換爲 C。第二個類型映射(out 類型映射)用於向另外一個方向轉換。每一個類型映射的內容都是一小段代碼,直接插入 SWIG 生成的包裝器函數中。該代碼一般是 C 或 C++ 代碼,它們將生成到 C/C++ 包裝器函數中。請注意,並不是老是如此,由於某些目標語言模塊容許類型映射中的目標語言代碼生成到目標語言特定的文件中。在此代碼中,將擴展許多帶有 $ 前綴的特殊變量。這些實際上只是 C/C++ 變量的佔位符,這些變量是在建立包裝器函數的過程當中生成的。在這種狀況下,$input 是指須要轉換爲 C/C++ 的輸入對象,而 $result 是指將由包裝器函數返回的對象。$1 指的是一個 C/C++ 變量,其類型與類型映射聲明中指定的類型相同(本例中爲 int)。

一個簡短的示例可能會使這一點更加清楚。若是要包裝這樣的函數:

int gcd(int x, int y);

A wrapper function would look approximately like this:

包裝器函數大體以下所示:

PyObject *wrap_gcd(PyObject *self, PyObject *args) {
  int arg1;
  int arg2;
  int result;
  PyObject *obj1;
  PyObject *obj2;
  PyObject *resultobj;

  if (!PyArg_ParseTuple("OO:gcd", &obj1, &obj2)) return NULL;

  /* "in" typemap, argument 1 */
  {
    arg1 = PyInt_AsLong(obj1);
  }

  /* "in" typemap, argument 2 */
  {
    arg2 = PyInt_AsLong(obj2);
  }

  result = gcd(arg1, arg2);

  /* "out" typemap, return value */
  {
    resultobj = PyInt_FromLong(result);
  }

  return resultobj;
}

In this code, you can see how the typemap code has been inserted into the function. You can also see how the special $ variables have been expanded to match certain variable names inside the wrapper function. This is really the whole idea behind typemaps--they simply let you insert arbitrary code into different parts of the generated wrapper functions. Because arbitrary code can be inserted, it possible to completely change the way in which values are converted.

在此代碼中,你能夠看到如何將類型映射代碼插入到函數中。你還能夠看到特殊的 $ 變量是如何擴展的,以匹配包裝器函數中的某些變量名稱。這實際上就是類型映射背後的所有思想,它們只是讓你將任意代碼插入生成的包裝器函數的不一樣部分。因爲能夠插入任意代碼,所以能夠徹底改變值轉換的方式。

11.1.3 模式匹配

As the name implies, the purpose of a typemap is to "map" C datatypes to types in the target language. Once a typemap is defined for a C datatype, it is applied to all future occurrences of that type in the input file. For example:

顧名思義,類型映射的目的是將 C 數據類型「映射」爲目標語言中的類型。一旦爲 C 數據類型定義類型映射,它將應用於輸入文件中出現的全部該類型。例如:

/* Convert from Perl --> C */
%typemap(in) int {
  $1 = SvIV($input);
}

...
int factorial(int n);
int gcd(int x, int y);
int count(char *s, char *t, int max);

The matching of typemaps to C datatypes is more than a simple textual match. In fact, typemaps are fully built into the underlying type system. Therefore, typemaps are unaffected by typedef, namespaces, and other declarations that might hide the underlying type. For example, you could have code like this:

類型映射與 C 數據類型的匹配不只僅是簡單的文本匹配。實際上,類型映射徹底內置在基礎類型系統中。所以,類型映射不受 typedef、命名空間和其餘可能隱藏基礎類型的聲明的影響。例如,你可能具備如下代碼:

/* Convert from Ruby--> C */
%typemap(in) int {
  $1 = NUM2INT($input);
}
...
typedef int Integer;
namespace foo {
  typedef Integer Number;
};

int foo(int x);
int bar(Integer y);
int spam(foo::Number a, foo::Number b);

In this case, the typemap is still applied to the proper arguments even though typenames don't always match the text "int". This ability to track types is a critical part of SWIG--in fact, all of the target language modules work merely define a set of typemaps for the basic types. Yet, it is never necessary to write new typemaps for typenames introduced by typedef.

In addition to tracking typenames, typemaps may also be specialized to match against a specific argument name. For example, you could write a typemap like this:

在這種狀況下,即便類型名並不老是與文本 int 匹配,也仍然將類型映射應用於適當的參數。這種跟蹤類型的能力是 SWIG 的重要組成部分——實際上,全部目標語言模塊都只能爲基本類型定義一組類型映射。可是,歷來沒有必要爲 typedef 引入的類型名編寫新的類型映射。

除了跟蹤類型名稱以外,類型映射還能夠專門用於與特定的參數名稱匹配。例如,你能夠編寫這樣的類型映射:

%typemap(in) double nonnegative {
  $1 = PyFloat_AsDouble($input);
  if ($1 < 0) {
    PyErr_SetString(PyExc_ValueError, "argument must be nonnegative.");
    SWIG_fail;
  }
}

...
double sin(double x);
double cos(double x);
double sqrt(double nonnegative);

typedef double Real;
double log(Real nonnegative);
...

For certain tasks such as input argument conversion, typemaps can be defined for sequences of consecutive arguments. For example:

對於某些任務,例如輸入參數轉換,能夠爲連續參數序列定義類型映射。例如:

%typemap(in) (char *str, int len) {
  $1 = PyString_AsString($input);   /* char *str */
  $2 = PyString_Size($input);       /* int len   */
}
...
int count(char *str, int len, char c);

In this case, a single input object is expanded into a pair of C arguments. This example also provides a hint to the unusual variable naming scheme involving $1, $2, and so forth.

在這種狀況下,單個輸入對象將擴展爲一對 C 參數。這個例子也暗示了涉及不尋常的變量命名方案,包括 $1$2 等等。

11.1.4 複用類型映射

Typemaps are normally defined for specific type and argument name patterns. However, typemaps can also be copied and reused. One way to do this is to use assignment like this:

類型映射一般爲特定的類型和參數名稱模式而定義。可是,類型映射也能夠複製和重用。一種方法是使用賦值:

%typemap(in) Integer = int;
%typemap(in) (char *buffer, int size) = (char *str, int len);

A more general form of copying is found in the %apply directive like this:

%apply 指令中能夠找到更通用的複製形式,以下所示:

%typemap(in) int {
  /* Convert an integer argument */
  ...
}
%typemap(out) int {
  /* Return an integer value */
  ...
}

/* Apply all of the integer typemaps to size_t */
%apply int { size_t };

%apply merely takes all of the typemaps that are defined for one type and applies them to other types. Note: you can include a comma separated set of types in the {...} part of %apply.

It should be noted that it is not necessary to copy typemaps for types that are related by typedef. For example, if you have this,

%apply 僅接受爲一種類型定義的全部類型映射,並將它們應用於其餘類型。注意:你能夠在 %apply{...} 部分中包含一組用逗號分隔的類型。

應該注意的是,沒有必要爲 typedef 相關的類型複製類型映射。例如,若是你有這個,

typedef int size_t;

then SWIG already knows that the int typemaps apply. You don't have to do anything.

那麼 SWIG 已經知道了 int 的類型映射。你沒必要作任何事情。

11.1.5 類型映射能幹什麼?

The primary use of typemaps is for defining wrapper generation behavior at the level of individual C/C++ datatypes. There are currently six general categories of problems that typemaps address:

類型映射的主要用途是在單一 C/C++ 數據類型級別上定義包裝器生成行爲。當前,類型映射解決了六大類問題:

Argument handling

參數處理

int foo(int x, double y, char *s);
  • Input argument conversion ("in" typemap).
  • Input argument type checking for types used in overloaded methods ("typecheck" typemap).
  • Output argument handling ("argout" typemap).
  • Input argument value checking ("check" typemap).
  • Input argument initialization ("arginit" typemap).
  • Default arguments ("default" typemap).
  • Input argument resource management ("freearg" typemap).
  • 輸入參數轉換(in 類型映射)。
  • 重載方法中的輸入參數類型檢查(typecheck 類型映射)。
  • 輸出參數處理(argout 類型映射)。
  • 輸入參數值檢查(check 類型映射)。
  • 輸入參數初始化(arginit 類型映射)。
  • 默認參數(default 類型映射)。
  • 輸入參數資源管理(freearg 類型映射)。

Return value handling

返回值處理

int foo(int x, double y, char *s);
  • Function return value conversion ("out" typemap).
  • Return value resource management ("ret" typemap).
  • Resource management for newly allocated objects ("newfree" typemap).
  • 函數返回值轉換(out 類型映射)。
  • 返回值資源管理(ret 類型映射)。
  • 新分配對象的資源管理(newfree 類型映射)。

Exception handling

異常處理

int foo(int x, double y, char *s) throw(MemoryError, IndexError);
  • Handling of C++ exception specifications. ("throw" typemap).
  • 處理 C++ 異常規範。(throw 類型映射)。

Global variables

全局變量

int foo;
  • Assignment of a global variable. ("varin" typemap).
  • Reading a global variable. ("varout" typemap).
  • 分配全局變量。(varin 類型映射)。
  • 讀取全局變量。(varout 類型映射)。

Member variables

成員變量

struct Foo {
  int x[20];
};
  • Assignment of data to a class/structure member. ("memberin" typemap).
  • 將數據分配給類或結構體成員。(memberin 類型映射)。

Constant creation

建立常量

#define FOO 3
%constant int BAR = 42;
enum { ALE, LAGER, STOUT };
  • Creation of constant values. ("consttab" or "constcode" typemap).

Details of each of these typemaps will be covered shortly. Also, certain language modules may define additional typemaps that expand upon this list. For example, the Java module defines a variety of typemaps for controlling additional aspects of the Java bindings. Consult language specific documentation for further details.

  • 建立常數值。(consttabconstcode 類型映射)。

每一個類型映射的詳細內容很快會提到。一樣,某些語言模塊可能會定義其餘類型映射以擴展此列表。例如,Java 模塊定義了各類類型映射來控制 Java 綁定的其餘方面。請查閱特定於語言的文檔以獲取更多詳細信息。

11.1.6 類型映射不能幹什麼?

Typemaps can't be used to define properties that apply to C/C++ declarations as a whole. For example, suppose you had a declaration like this,

類型映射不能用於定義總體上適用於 C/C++ 聲明的屬性。例如,假設你有一個這樣的聲明,

Foo *make_Foo(int n);

and you wanted to tell SWIG that make_Foo(int n) returned a newly allocated object (for the purposes of providing better memory management). Clearly, this property of make_Foo(int n) is not a property that would be associated with the datatype Foo * by itself. Therefore, a completely different SWIG customization mechanism (%feature) is used for this purpose. Consult the Customization Features chapter for more information about that.

Typemaps also can't be used to rearrange or transform the order of arguments. For example, if you had a function like this:

而且你想告訴 SWIG make_Foo(int n) 返回了一個新分配的對象(目的是提供更好的內存管理)。顯然,make_Foo(int n) 的此屬性不是自己將與數據類型 Foo * 相關聯的屬性。所以,爲此目的要使用徹底不一樣的 SWIG 定製機制(%feature)。有關更多信息,請參考自定義功能章節。

類型映射也不能用於從新排列或轉換參數的順序。例如,若是你具備以下函數:

void foo(int, char *);

you can't use typemaps to interchange the arguments, allowing you to call the function like this:

你不能使用類型映射來交換參數,進而容許你能這樣調用函數:

foo("hello", 3)          # Reversed arguments

If you want to change the calling conventions of a function, write a helper function instead. For example:

若是要更改函數的調用約定,請編寫輔助函數。例如:

%rename(foo) wrap_foo;
%inline %{
void wrap_foo(char *s, int x) {
  foo(x, s);
}
%}

11.1.7 與面向切面編程的類似之處

SWIG has parallels to Aspect Oriented Software Development (AOP). The AOP terminology with respect to SWIG typemaps can be viewed as follows:

  • Cross-cutting concerns: The cross-cutting concerns are the modularization of the functionality that the typemaps implement, which is primarily marshalling of types from/to the target language and C/C++.
  • Advice: The typemap body contains code which is executed whenever the marshalling is required.
  • Pointcut: The pointcuts are the positions in the wrapper code that the typemap code is generated into.
  • Aspect: Aspects are the combination of the pointcut and the advice, hence each typemap is an aspect.

SWIG can also be viewed as has having a second set of aspects based around %feature. Features such as %exception are also cross-cutting concerns as they encapsulate code that can be used to add logging or exception handling to any function.

SWIG 與面向切面的軟件開發(AOP)類似。與 SWIG 類型映射有關的 AOP 術語以下:

  • 橫切關注點:橫切關注點是類型映射所實現功能的模塊化,主要是將目標語言和 C/C++ 之間的類型進行編組。
  • 通知:類型映射主體包含在須要編組時執行的代碼。
  • 切入點:切入點是包裝器代碼中生成類型映射代碼的位置。
  • 切面:切面是切入點和通知的組合,所以每一個類型映射都是一個切面。

也能夠將 SWIG 視爲具備基於 %feature 的第二組切面。諸如 %exception 之類的功能也是橫切關注點,由於它們封裝了可用於向任何函數添加日誌記錄或異常處理的代碼。

11.1.8 本章的剩餘部分

The rest of this chapter provides detailed information for people who want to write new typemaps. This information is of particular importance to anyone who intends to write a new SWIG target language module. Power users can also use this information to write application specific type conversion rules.

Since typemaps are strongly tied to the underlying C++ type system, subsequent sections assume that you are reasonably familiar with the basic details of values, pointers, references, arrays, type qualifiers (e.g., const), structures, namespaces, templates, and memory management in C/C++. If not, you would be well-advised to consult a copy of "The C Programming Language" by Kernighan and Ritchie or "The C++ Programming Language" by Stroustrup before going any further.

本章的剩餘部分爲想要編寫新的類型映射的人提供了詳細的信息。對於打算爲 SWIG 編寫新目標語言模塊的人來講,這些信息都特別重要。高級用戶還可使用這些信息來編寫應用程序特定的類型轉換規則。

因爲類型映射與底層 C++ 類型系統緊密相關,所以後續章節假定你對值、指針、引用、數組、類型限定符(例如 const)、結構體、命名空間、模板和 C/C++ 中的內存管理至關熟悉。若是不是這樣,建議你先閱讀 Kernighan 和 Ritchie 撰寫的《The C Programming Language》或 Stroustrup 撰寫的《The C++ Programming Language》。

11.2 類型映射詳述

This section describes the behavior of the %typemap directive itself.

本節描述了 %typemap 指令自己的行爲。

11.2.1 定義一個類型映射

New typemaps are defined using the %typemap declaration. The general form of this declaration is as follows (parts enclosed in [...] are optional):

新的類型映射使用 %typemap 聲明定義。該聲明的通常形式以下([...] 中的部分是可選的):

%typemap(method [, modifiers]) typelist code ;

method is a simply a name that specifies what kind of typemap is being defined. It is usually a name like "in", "out", or "argout". The purpose of these methods is described later.

modifiers is an optional comma separated list of name="value" values. These are sometimes to attach extra information to a typemap and is often target-language dependent. They are also known as typemap attributes.

typelist is a list of the C++ type patterns that the typemap will match. The general form of this list is as follows:

method 是一個簡單的名稱,用於指定要定義的類型映射。一般,它的名稱相似於 inoutargout。這些方法的目的將在後面說明。

modifiers 是一個可選的逗號分隔列表,其中包含 name="value" 值。這些有時會在類型映射上附加額外的信息,而且一般取決於目標語言。它們也稱爲類型映射屬性。

typelist 是類型映射將匹配的 C++ 類型模式的列表。此列表的通常形式以下:

typelist    :  typepattern [, typepattern, typepattern, ... ] ;

typepattern :  type [ (parms) ]
            |  type name [ (parms) ]
            |  ( typelist ) [ (parms) ]

Each type pattern is either a simple type, a simple type and argument name, or a list of types in the case of multi-argument typemaps. In addition, each type pattern can be parameterized with a list of temporary variables (parms). The purpose of these variables will be explained shortly.

code specifies the code used in the typemap. Usually this is C/C++ code, but in the statically typed target languages, such as Java and C#, this can contain target language code for certain typemaps. It can take any one of the following forms:

每一個類型模式能夠是簡單類型、簡單類型和參數名稱,或者在多參數類型映射下的類型列表。此外,可使用一系列臨時變量(參數)對每一個類型模式進行參數化。這些變量的目的將在稍後說明。

code 指定類型映射中使用的代碼。一般這是 C/C++ 代碼,可是在靜態類型的目標語言(例如 Java 和 C#)中,它能夠包含某些類型映射的目標語言代碼。能夠採用如下任何一種形式:

code       : { ... }
           | " ... "
           | %{ ... %}

Note that the preprocessor will expand code within the {} delimiters, but not in the last two styles of delimiters, see Preprocessor and Typemaps. Here are some examples of valid typemap specifications:

請注意,預處理器將在 {} 分隔符內擴展代碼,但不會在最後兩種分隔符樣式中擴展代碼,請參閱《預處理器與類型映射》章節。如下是有效類型映射規範的一些示例:

/* Simple typemap declarations */
%typemap(in) int {
  $1 = PyInt_AsLong($input);
}
%typemap(in) int "$1 = PyInt_AsLong($input);";
%typemap(in) int %{
  $1 = PyInt_AsLong($input);
%}

/* Typemap with extra argument name */
%typemap(in) int nonnegative {
  ...
}

/* Multiple types in one typemap */
%typemap(in) int, short, long {
  $1 = SvIV($input);
}

/* Typemap with modifiers */
%typemap(in, doc="integer") int "$1 = scm_to_int($input);";

/* Typemap applied to patterns of multiple arguments */
%typemap(in) (char *str, int len),
             (char *buffer, int size)
{
  $1 = PyString_AsString($input);
  $2 = PyString_Size($input);
}

/* Typemap with extra pattern parameters */
%typemap(in, numinputs=0) int *output (int temp),
                          long *output (long temp)
{
  $1 = &temp;
}

Admittedly, it's not the most readable syntax at first glance. However, the purpose of the individual pieces will become clear.

乍一看,這並非最易讀的語法。可是,各個部分的目的將變得清楚。

11.2.2 類型映射做用範圍

Once defined, a typemap remains in effect for all of the declarations that follow. A typemap may be redefined for different sections of an input file. For example:

定義後,類型映射對於隨後出現的全部聲明都有效。能夠爲輸入文件的不一樣部分從新定義類型映射。例如:

// typemap1
%typemap(in) int {
...
}

int fact(int);                    // typemap1
int gcd(int x, int y);            // typemap1

// typemap2
%typemap(in) int {
...
}

int isprime(int);                 // typemap2

One exception to the typemap scoping rules pertains to the %extend declaration. %extend is used to attach new declarations to a class or structure definition. Because of this, all of the declarations in an %extend block are subject to the typemap rules that are in effect at the point where the class itself is defined. For example:

類型映射範圍規則的一個例外與 %extend 聲明有關。%extend 用於將新的聲明附加到類或結構體定義上。所以,%extend 塊中的全部聲明都將受到類型映射規則的約束,該規則在定義類自己時生效。例如:

class Foo {
  ...
};

%typemap(in) int {
 ...
}

%extend Foo {
  int blah(int x);    // typemap has no effect.  Declaration is attached to Foo which
                      // appears before the %typemap declaration.
};

11.2.3 複製類型映射

A typemap is copied by using assignment. For example:

使用賦值複製類型映射。例如:

%typemap(in) Integer = int;

or this:

或者這樣:

%typemap(in) Integer, Number, int32_t = int;

Types are often managed by a collection of different typemaps. For example:

類型一般由不一樣類型映射的集合來管理。例如:

%typemap(in)     int { ... }
%typemap(out)    int { ... }
%typemap(varin)  int { ... }
%typemap(varout) int { ... }

To copy all of these typemaps to a new type, use %apply. For example:

要將全部這些類型映射覆制到一個新的類型,請使用 %apply。例如:

%apply int { Integer };            // Copy all int typemaps to Integer
%apply int { Integer, Number };    // Copy all int typemaps to both Integer and Number

The patterns for %apply follow the same rules as for %typemap. For example:

%apply 的模式遵循與 %typemap 相同的規則。例如:

%apply int *output { Integer *output };                    // Typemap with name
%apply (char *buf, int len) { (char *buffer, int size) };  // Multiple arguments

11.2.4 刪除類型映射

A typemap can be deleted by simply defining no code. For example:

不須要定義代碼便可刪除類型映射。例如:

%typemap(in) int;               // Clears typemap for int
%typemap(in) int, long, short;  // Clears typemap for int, long, short
%typemap(in) int *output;

The %clear directive clears all typemaps for a given type. For example:

%clear 指令清除給定類型的全部類型映射。例如:

%clear int;                     // Removes all types for int
%clear int *output, long *output;

Note: Since SWIG's default behavior is defined by typemaps, clearing a fundamental type like int will make that type unusable unless you also define a new set of typemaps immediately after the clear operation.

注意:因爲 SWIG 的默認行爲是由類型映射定義的,所以除非清除操做以後當即定義了一組新的類型映射,不然清除基本類型(如 int)將使該類型不可用。

11.2.5 類型映射的位置

Typemap declarations can be declared in the global scope, within a C++ namespace, and within a C++ class. For example:

能夠在全局範圍、C++ 命名空間和 C++ 類中聲明類型映射。例如:

%typemap(in) int {
  ...
}

namespace std {
  class string;
  %typemap(in) string {
    ...
  }
}

class Bar {
public:
  typedef const int & const_reference;
  %typemap(out) const_reference {
    ...
  }
};

When a typemap appears inside a namespace or class, it stays in effect until the end of the SWIG input (just like before). However, the typemap takes the local scope into account. Therefore, this code

當類型映射出如今命名空間或類中時,它直到 SWIG 輸入文件的結束(就像以前同樣)一直有效。可是,類型映射將局部範圍考慮在內。所以,此代碼

namespace std {
  class string;
  %typemap(in) string {
    ...
  }
}

is really defining a typemap for the type std::string. You could have code like this:

確實爲 std::string 類型定義了一個類型映射。你可能會有這樣的代碼:

namespace std {
  class string;
  %typemap(in) string {          /* std::string */
    ...
  }
}

namespace Foo {
  class string;
  %typemap(in) string {          /* Foo::string */
    ...
  }
}

In this case, there are two completely distinct typemaps that apply to two completely different types (std::string and Foo::string).

It should be noted that for scoping to work, SWIG has to know that string is a typename defined within a particular namespace. In this example, this is done using the forward class declaration class string.

在這種狀況下,有兩個徹底不一樣的類型映射適用於兩個徹底不一樣的類型(std::stringFoo::string)。

應當注意,爲使做用域有效,SWIG 必須知道 string 是在特定名稱空間內定義的類型名。在此示例中,這是使用正向類聲明 class string 完成的。

11.3 模式匹配規則

The section describes the pattern matching rules by which C/C++ datatypes are associated with typemaps. The matching rules can be observed in practice by using the debugging options also described.

本節描述了模式匹配規則,經過這些規則,C/C++ 數據類型與類型映射相關聯。實際中,能夠經過使用調試選項來觀察匹配規則。

11.3.1 基本匹配規則

Typemaps are matched using both a type and a name (typically the name of a argument). For a given TYPE NAME pair, the following rules are applied, in order, to find a match. The first typemap found is used.

  • Typemaps that exactly match TYPE and NAME.
  • Typemaps that exactly match TYPE only.
  • If TYPE is a C++ template of type T<TPARMS>, where TPARMS are the template parameters, the type is stripped of the template parameters and the following checks are then made:
    • Typemaps that exactly match T and NAME.
    • Typemaps that exactly match T only.

If TYPE includes qualifiers (const, volatile, etc.), each qualifier is stripped one at a time to form a new stripped type and the matching rules above are repeated on the stripped type. The left-most qualifier is stripped first, resulting in the right-most (or top-level) qualifier being stripped last. For example int const*const is first stripped to int *const then int *.

If TYPE is an array. The following transformation is made:

  • Replace all dimensions to [ANY] and look for a generic array typemap.

To illustrate, suppose that you had a function like this:

使用類型和名稱(一般是參數名稱)來匹配類型映射。對於給定的 TYPE NAME 配對,將應用如下規則來查找匹配項。第一個找到的類型映射將被使用。

  • TYPENAME 徹底匹配的類型映射。
  • 僅與 TYPE 徹底匹配的類型映射。
  • 若是 TYPET<TPARMS> 類型的 C++ 模板,其中 TPARMS 是模板參數,則將類型的模板參數剝離,而後進行如下檢查:
    • TNAME 徹底匹配的類型映射。
    • 僅與 T 徹底匹配的類型映射。

若是 TYPE 包含限定符(constvolatile 等),則每次剝離一個限定符以造成新的剝離類型,並在剝離類型上重複上述匹配規則。最左邊的限定符首先被剝離,最右邊的(或頂級)限定符最後被剝離。例如,首先將 int const * const 剝離爲 int * const,而後剝離爲 int *

若是 TYPE 是一個數組。進行如下轉換:

  • 將全部維度替換爲 [ANY],並查找通用數組類型映射

爲了說明這一點,假設你具備以下函數:

int foo(const char *s);

To find a typemap for the argument const char *s, SWIG will search for the following typemaps:

要爲參數 const char *s 查找類型映射,SWIG 將搜索如下類型映射:

const char *s           Exact type and name match
const char *            Exact type match
char *s                 Type and name match (qualifier stripped)
char *                  Type match (qualifier stripped)

When more than one typemap rule might be defined, only the first match found is actually used. Here is an example that shows how some of the basic rules are applied:

當可能定義多個類型映射規則時,實際上僅使用找到的第一個匹配項。下面是一個示例,顯示瞭如何應用一些基本規則:

%typemap(in) int *x {
  ... typemap 1
}

%typemap(in) int * {
  ... typemap 2
}

%typemap(in) const int *z {
  ... typemap 3
}

%typemap(in) int [4] {
  ... typemap 4
}

%typemap(in) int [ANY] {
  ... typemap 5
}

void A(int *x);        // int *x rule       (typemap 1)
void B(int *y);        // int * rule        (typemap 2)
void C(const int *x);  // int *x rule       (typemap 1)
void D(const int *z);  // const int *z rule (typemap 3)
void E(int x[4]);      // int [4] rule      (typemap 4)
void F(int x[1000]);   // int [ANY] rule    (typemap 5)

Compatibility note: SWIG-2.0.0 introduced stripping the qualifiers one step at a time. Prior versions stripped all qualifiers in one step.

注意兼容性:SWIG-2.0.0 引入了一次刪除一個限定符。先前的版本一次就消除了全部限定符。

11.3.2 typedef 還原匹配

If no match is found using the rules in the previous section, SWIG applies a typedef reduction to the type and repeats the typemap search for the reduced type. To illustrate, suppose you had code like this:

若是使用上一節中的規則未找到匹配項,則 SWIG 將 typedef 還原,而後對還原後的類型重複進行類型映射搜索。爲了說明這一點,假設你有以下代碼:

%typemap(in) int {
  ... typemap 1
}

typedef int Integer;
void blah(Integer x);

To find the typemap for Integer x, SWIG will first search for the following typemaps:

爲了找到 Integer x 的類型映射,SWIG 將首先搜索如下類型映射:

Integer x
Integer

Finding no match, it then applies a reduction Integer -> int to the type and repeats the search.

若是找不到匹配項,則對類型應用還原 Integer -> int,並重復搜索。

int x
int      --> match: typemap 1

Even though two types might be the same via typedef, SWIG allows typemaps to be defined for each typename independently. This allows for interesting customization possibilities based solely on the typename itself. For example, you could write code like this:

即便兩個類型經過 typedef 多是相同的,SWIG 仍容許爲每一個類型名分別定義類型映射。這容許僅基於類型名稱自己進行有趣的自定義。例如,你能夠編寫以下代碼:

typedef double  pdouble;     // Positive double

// typemap 1
%typemap(in) double {
  ... get a double ...
}
// typemap 2
%typemap(in) pdouble {
  ... get a positive double ...
}
double sin(double x);           // typemap 1
pdouble sqrt(pdouble x);        // typemap 2

When reducing the type, only one typedef reduction is applied at a time. The search process continues to apply reductions until a match is found or until no more reductions can be made.

For complicated types, the reduction process can generate a long list of patterns. Consider the following:

還原類型時,一次僅還原一次 typedef。搜索過程將繼續應用還原直到找到匹配項,或沒法再進行還原。

對於複雜類型,還原過程能夠生成一長串模式。考慮如下:

typedef int Integer;
typedef Integer Row4[4];
void foo(Row4 rows[10]);

To find a match for the Row4 rows[10] argument, SWIG would check the following patterns, stopping only when it found a match:

爲了找到 Row4 rows[10] 參數的匹配項,SWIG 將檢查如下模式,僅在找到匹配項時中止:

Row4 rows[10]
Row4 [10]
Row4 rows[ANY]
Row4 [ANY]

# Reduce Row4 --> Integer[4]
Integer rows[10][4]
Integer [10][4]
Integer rows[ANY][ANY]
Integer [ANY][ANY]

# Reduce Integer --> int
int rows[10][4]
int [10][4]
int rows[ANY][ANY]
int [ANY][ANY]

For parameterized types like templates, the situation is even more complicated. Suppose you had some declarations like this:

對於像模板這樣的參數化類型,狀況甚至更加複雜。假設你有一些這樣的聲明:

typedef int Integer;
typedef foo<Integer, Integer> fooii;
void blah(fooii *x);

In this case, the following typemap patterns are searched for the argument fooii *x:

在這種狀況下,將在如下類型映射模式中搜索參數 fooii *x

fooii *x
fooii *

# Reduce fooii --> foo<Integer, Integer>
foo<Integer, Integer> *x
foo<Integer, Integer> *

# Reduce Integer -> int
foo<int, Integer> *x
foo<int, Integer> *

# Reduce Integer -> int
foo<int, int> *x
foo<int, int> *

Typemap reductions are always applied to the left-most type that appears. Only when no reductions can be made to the left-most type are reductions made to other parts of the type. This behavior means that you could define a typemap for foo<int, Integer>, but a typemap for foo<Integer, int> would never be matched. Admittedly, this is rather esoteric--there's little practical reason to write a typemap quite like that. Of course, you could rely on this to confuse your coworkers even more.

As a point of clarification, it is worth emphasizing that typedef matching is a typedef reduction process only, that is, SWIG does not search for every single possible typedef. Given a type in a declaration, it will only reduce the type, it won't build it up looking for typedefs. For example, given the type Struct, the typemap below will not be used for the aStruct parameter, because Struct is fully reduced:

還原類型映射始終應用於出如今最左側的類型。僅當沒法對最左邊的類型進行還原時,纔對類型的其餘部分進行還原。這種行爲意味着你能夠爲 foo<int, Integer> 定義一個類型映射,可是 foo<Integer, int> 的類型映射不會被匹配。誠然,這是至關不常見的——幾乎沒有實際的理由來編寫相似的類型映射。固然,你能夠用它使你的同事更加困惑。

須要澄清一點,值得強調的是 typedef 匹配僅是 typedef 的「還原」過程,也就是說,SWIG 不會搜索每一個可能的 typedef。給定聲明中的類型,它只會還原類型,而不會在尋找 typedef 時創建它。例如,給定類型爲 Struct,因爲 Struct 已被徹底還原,所以如下類型映射將不會用於 aStruct 參數:

struct Struct {...};
typedef Struct StructTypedef;

%typemap(in) StructTypedef {
  ...
}

void go(Struct aStruct);

11.3.3 默認類型映射匹配規則

If the basic pattern matching rules result in no match being made, even after typedef reductions, the default typemap matching rules are used to look for a suitable typemap match. These rules match a generic typemap based on the reserved SWIGTYPE base type. For example pointers will use SWIGTYPE *and references will use SWIGTYPE &. More precisely, the rules are based on the C++ class template partial specialization matching rules used by C++ compilers when looking for an appropriate partial template specialization. This means that a match is chosen from the most specialized set of generic typemap types available. For example, when looking for a match to int const *, the rules will prefer to match SWIGTYPE const * if available before matching SWIGTYPE *, before matching SWIGTYPE.

Most SWIG language modules use typemaps to define the default behavior of the C primitive types. This is entirely straightforward. For example, a set of typemaps for primitives marshalled by value or const reference are written like this:

若是即便在還原 typedef 以後基本模式匹配規則最終沒有匹配,將使用默認的類型映射匹配規則來尋找合適的匹配。這些規則匹配基於保留的 SWIGTYPE 基本類型的通用類型映射。例如,指針將使用 SWIGTYPE *,而引用將使用 SWIGTYPE &。更準確地說,這些規則基於 C++ 類模板偏特化匹配規則,這些匹配規則由 C++ 編譯器在尋找合適的模板偏特化時使用。這意味着從可用的最特定的通用類型映射類型集合中選擇一個匹配項。例如,當尋找與 int const * 的匹配項時,規則將優先匹配 SWIGTYPE const *(若是有的話),而後再匹配 SWIGTYPE *,再匹配 SWIGTYPE

大多數 SWIG 語言模塊都使用類型映射來定義 C 基本類型的默認行爲。這是很是簡單的。例如,按值或常引用編組的原始類型的一組類型映射以下所示:

%typemap(in) int           "... convert to int ...";
%typemap(in) short         "... convert to short ...";
%typemap(in) float         "... convert to float ...";
...
%typemap(in) const int &   "... convert ...";
%typemap(in) const short & "... convert ...";
%typemap(in) const float & "... convert ...";
...

Since typemap matching follows all typedef declarations, any sort of type that is mapped to a primitive type by value or const reference through typedef will be picked up by one of these primitive typemaps. Most language modules also define typemaps for char pointers and char arrays to handle strings, so these non-default types will also be used in preference as the basic typemap matching rules provide a better match than the default typemap matching rules.

Below is a list of the typical default types supplied by language modules, showing what the "in" typemap would look like:

因爲類型映射匹配遵循全部的 typedef 聲明,所以經過 typedef 經過值或常引用映射到原始類型的任何類型的類型都將被這些原始類型映射之一所拾取。大多數語言模塊還爲 char 指針和 char 數組定義了類型映射以處理字符串,所以這些非默認類型也將優先使用,由於基本的類型映射匹配規則比默認的類型映射匹配規則提供了更好的匹配。

下面是語言模塊提供的典型默認類型的列表,顯示了 in 類型映射的樣子:

%typemap(in) SWIGTYPE &            { ... default reference handling ...                       };
%typemap(in) SWIGTYPE *            { ... default pointer handling ...                         };
%typemap(in) SWIGTYPE *const       { ... default pointer const handling ...                   };
%typemap(in) SWIGTYPE *const&      { ... default pointer const reference handling ...         };
%typemap(in) SWIGTYPE[ANY]         { ... 1D fixed size arrays handlling ...                   };
%typemap(in) SWIGTYPE []           { ... unknown sized array handling ...                     };
%typemap(in) enum SWIGTYPE         { ... default handling for enum values ...                 };
%typemap(in) const enum SWIGTYPE & { ... default handling for const enum reference values ... };
%typemap(in) SWIGTYPE (CLASS::*)   { ... default pointer member handling ...                  };
%typemap(in) SWIGTYPE              { ... simple default handling ...                          };

If you wanted to change SWIG's default handling for simple pointers, you would simply redefine the rule for SWIGTYPE *. Note, the simple default typemap rule is used to match against simple types that don't match any other rules:

若是你想更改 SWIG 對簡單指針的默認處理,很簡單,只需爲 SWIGTYPE * 從新定義規則。請注意,簡單的默認類型映射規則用於與不匹配任何其餘規則的簡單類型進行匹配:

%typemap(in) SWIGTYPE              { ... simple default handling ...                          }

This typemap is important because it is the rule that gets triggered when call or return by value is used. For instance, if you have a declaration like this:

此類型映射很重要,由於使用調用或按值返回時會觸發該規則。例如,若是你有這樣的聲明:

double dot_product(Vector a, Vector b);

The Vector type will usually just get matched against SWIGTYPE. The default implementation of SWIGTYPEis to convert the value into pointers (as described in this earlier section).

By redefining SWIGTYPE it may be possible to implement other behavior. For example, if you cleared all typemaps for SWIGTYPE, SWIG simply won't wrap any unknown datatype (which might be useful for debugging). Alternatively, you might modify SWIGTYPE to marshal objects into strings instead of converting them to pointers.

Let's consider an example where the following typemaps are defined and SWIG is looking for the best match for the enum shown below:

Vector 類型一般只會與 SWIGTYPE 相匹配。SWIGTYPE 的默認實現是將值轉換爲指針(如本以前的章節所述)。

經過從新定義 SWIGTYPE,能夠實現其餘行爲。例如,若是你清除了 SWIGTYPE 的全部類型映射,則 SWIG 不會包裝任何未知的數據類型(這可能對調試頗有用)。或者,你能夠修改 SWIGTYPE 以將對象編組爲字符串,而不是將它們轉換爲指針。

讓咱們考慮一個示例,其中定義瞭如下類型映射,而且 SWIG 正在爲如下所示的枚舉尋找最佳匹配:

%typemap(in) const Hello &          { ... }
%typemap(in) const enum SWIGTYPE &  { ... }
%typemap(in) enum SWIGTYPE &        { ... }
%typemap(in) SWIGTYPE &             { ... }
%typemap(in) SWIGTYPE               { ... }

enum Hello {};
const Hello &hi;

The typemap at the top of the list will be chosen, not because it is defined first, but because it is the closest match for the type being wrapped. If any of the typemaps in the above list were not defined, then the next one on the list would have precedence.

The best way to explore the default typemaps is to look at the ones already defined for a particular language module. Typemap definitions are usually found in the SWIG library in a file such as java.swg, csharp.swg etc. However, for many of the target languages the typemaps are hidden behind complicated macros, so the best way to view the default typemaps, or any typemaps for that matter, is to look at the preprocessed output by running swig -E on any interface file. Finally the best way to view the typemap matching rules in action is via the debugging typemap pattern matching options covered later on.

Compatibility note: The default typemap matching rules were modified in SWIG-2.0.0 from a slightly simpler scheme to match the current C++ class template partial specialization matching rules.

將選擇列表頂部的類型映射,不只是由於首先定義了它,並且是由於它與被包裝的類型最匹配。若是上面列表中的任何類型映射未定義,則列表中的下一個優先。

探索默認類型映射的最佳方法是查看已爲特定語言模塊定義的映射。類型映射定義一般能夠在 SWIG 庫的 java.swgcsharp.swg 等文件中找到。可是,對於許多目標語言而言,類型映射都隱藏在複雜的宏後面,所以,查看默認類型映射或任何與此相關的類型映射的最佳方法是在任何接口文件上運行 swig -E 來查看預處理後的輸出。最後,查看正在使用的類型映射匹配規則的最佳方法是經過稍後介紹的調試類型映射匹配模式選項。

注意兼容性:默認的類型映射匹配規則是在 SWIG-2.0.0 中從稍微簡單的方案中修改的,以匹配當前的 C++ 類模板偏特化匹配規則。

11.3.4 多參數類型映射

When multi-argument typemaps are specified, they take precedence over any typemaps specified for a single type. For example:

指定多參數類型映射時,它們優先於爲單個類型指定的任何類型映射。例如:

%typemap(in) (char *buffer, int len) {
  // typemap 1
}

%typemap(in) char *buffer {
  // typemap 2
}

void foo(char *buffer, int len, int count); // (char *buffer, int len)
void bar(char *buffer, int blah);           // char *buffer

Multi-argument typemaps are also more restrictive in the way that they are matched. Currently, the first argument follows the matching rules described in the previous section, but all subsequent arguments must match exactly.

多參數類型映射在匹配方式上也有更多限制。當前,第一個參數遵循上一節中描述的匹配規則,可是全部後續參數必須徹底匹配。

11.3.5 匹配規則對比 C++ 模板

For those intimately familiar with C++ templates, a comparison of the typemap matching rules and template type deduction is interesting. The two areas considered are firstly the default typemaps and their similarities to partial template specialization and secondly, non-default typemaps and their similarities to full template specialization.

For default (SWIGTYPE) typemaps the rules are inspired by C++ class template partial specialization. For example, given partial specialization for T const& :

對於那些熟悉 C++ 模板的人來講,比較類型映射匹配規則和模板類型推導是頗有趣的。首先考慮的兩個方面是默認類型映射及其與模板偏特化的類似性,其次是非默認類型映射及其與模板徹底化的類似性。

對於默認(SWIGTYPE)類型映射,規則受 C++ 類模板偏特化的啓發。例如,給定 T const& 的偏特化:

template <typename T> struct X             { void a(); };
template <typename T> struct X< T const& > { void b(); };

The full (unspecialized) template is matched with most types, such as:

徹底(非偏)模板與大多數類型匹配,例如:

X< int & >            x1;  x1.a();

and the following all match the T const& partial specialization:

以及如下全部匹配 T const& 的偏特化的代碼:

X< int *const& >      x2;  x2.b();
X< int const*const& > x3;  x3.b();
X< int const& >       x4;  x4.b();

Now, given just these two default typemaps, where T is analogous to SWIGTYPE:

如今,僅給出這兩個默認類型映射,其中 T 相似於 SWIGTYPE

%typemap(...) SWIGTYPE        { ... }
%typemap(...) SWIGTYPE const& { ... }

The generic default typemap SWIGTYPE is used with most types, such as

通用默認類型映射 SWIGTYPE 用於大多數類型,例如

int &

and the following all match the SWIGTYPE const& typemap, just like the partial template matching:

而且如下全部內容都匹配 SWIGTYPE const& 類型映射,就像部分模板匹配同樣:

int *const&
int const*const&
int const&

Note that the template and typemap matching rules are not identical for all default typemaps though, for example, with arrays.

For non-default typemaps, one might expect SWIG to follow the fully specialized template rules. This is nearly the case, but not quite. Consider a very similar example to the earlier partially specialized template but this time there is a fully specialized template:

請注意,模板和類型映射匹配規則對於全部默認類型映射都不相同,例如,對於數組。

對於非默認類型映射,可能但願 SWIG 遵循徹底特化的模板規則。這幾乎是事實,但事實並不是如此。考慮一個與早期的偏特化模板很是類似的示例,可是此次有一個徹底特化的模板:

template <typename T> struct Y       { void a(); };
template <> struct Y< int const & >  { void b(); };

Only the one type matches the specialized template exactly:

只有一種類型與特化模板徹底匹配:

Y< int & >             y1;  y1.a();
Y< int *const& >       y2;  y2.a();
Y< int const *const& > y3;  y3.a();
Y< int const& >        y4;  y4.b(); // fully specialized match

Given typemaps with the same types used for the template declared above, where T is again analogous to SWIGTYPE:

給定具備與上面聲明的模板相同類型的類型映射,其中 T 再次相似於 SWIGTYPE

%typemap(...) SWIGTYPE        { ... }
%typemap(...) int const&      { ... }

The comparison between non-default typemaps and fully specialized single parameter templates turns out to be the same, as just the one type will match the non-default typemap:

事實證實,非默認類型映射和徹底特化的單參數模板之間的比較是相同的,由於只有一種類型會匹配非默認類型映射:

int &
int *const&
int const*const&
int const&        // matches non-default typemap int const&

However, if a non-const type is used instead:

可是,若是改用很是量類型:

%typemap(...) SWIGTYPE        { ... }
%typemap(...) int &           { ... }

then there is a clear difference to template matching as both the const and non-const types match the typemap:

那麼模板匹配有明顯的區別,由於 const 和非 const 類型都與類型映射匹配:

int &             // matches non-default typemap int &
int *const&
int const*const&
int const&        // matches non-default typemap int &

There are other subtle differences such as typedef handling, but at least it should be clear that the typemap matching rules are similar to those for specialized template handling.

還有其餘一些細微的差別,例如 typedef 處理,但至少應該清楚的是,類型映射匹配規則相似於特化模板處理的規則。

11.3.6 調試類型映射模式匹配

There are two useful debug command line options available for debugging typemaps, -debug-tmsearchand -debug-tmused.

The -debug-tmsearch option is a verbose option for debugging typemap searches. This can be very useful for watching the pattern matching process in action and for debugging which typemaps are used. The option displays all the typemaps and types that are looked for until a successful pattern match is made. As the display includes searches for each and every type needed for wrapping, the amount of information displayed can be large. Normally you would manually search through the displayed information for the particular type that you are interested in.

For example, consider some of the code used in the Typedef reductions section already covered:

有兩個有用的調試命令行選項可用於調試類型映射:-debug-tmsearch-debug-tmused

-debug-tmsearch 選項是用於調試類型映射搜索的詳細選項。這對於觀察實際的模式匹配過程,以及調試使用哪一種類型映射很是有用。該選項顯示在成功進行模式匹配以前要查找的全部類型映射和類型。因爲顯示內容包括對包裝所需的每種類型的搜索,所以顯示的信息量可能很大。一般,你將在顯示的信息中手動搜索感興趣的特定類型。

例如,考慮已經討論過的還原 typedef章節中使用的一些代碼:

typedef int Integer;
typedef Integer Row4[4];
void foo(Row4 rows[10]);

A sample of the debugging output is shown below for the "in" typemap:

下面顯示了 in 類型映射的調試輸出示例:

swig -perl -debug-tmsearch example.i
...
example.h:3: Searching for a suitable 'in' typemap for: Row4 rows[10]
  Looking for: Row4 rows[10]
  Looking for: Row4 [10]
  Looking for: Row4 rows[ANY]
  Looking for: Row4 [ANY]
  Looking for: Integer rows[10][4]
  Looking for: Integer [10][4]
  Looking for: Integer rows[ANY][ANY]
  Looking for: Integer [ANY][ANY]
  Looking for: int rows[10][4]
  Looking for: int [10][4]
  Looking for: int rows[ANY][ANY]
  Looking for: int [ANY][ANY]
  Looking for: SWIGTYPE rows[ANY][ANY]
  Looking for: SWIGTYPE [ANY][ANY]
  Looking for: SWIGTYPE rows[ANY][]
  Looking for: SWIGTYPE [ANY][]
  Looking for: SWIGTYPE *rows[ANY]
  Looking for: SWIGTYPE *[ANY]
  Looking for: SWIGTYPE rows[ANY]
  Looking for: SWIGTYPE [ANY]
  Looking for: SWIGTYPE rows[]
  Looking for: SWIGTYPE []
  Using: %typemap(in) SWIGTYPE []
...

showing that the best default match supplied by SWIG is the SWIGTYPE [] typemap. As the example shows, the successful match displays the used typemap source including typemap method, type and optional name in one of these simplified formats:

  • Using: %typemap(method) type 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 }

此信息可能知足你的調試需求,可是,你可能須要進一步分析。若是接下來使用 -E 選項調用 SWIG 以顯示預處理後的輸出,並搜索所使用的特定類型映射,則將找到完整的類型映射內容(如下示例顯示在 Python 中):

%typemap(in, noblock=1) SWIGTYPE [] (void *argp = 0, int res = 0) {
  res = SWIG_ConvertPtr($input, &argp, $descriptor, $disown |  0 );
  if (!SWIG_IsOK(res)) {
    SWIG_exception_fail(SWIG_ArgError(res), "in method '" "$symname" "', argument "
                       "$argnum"" of type '" "$type""'");
  }
  $1 = ($ltype)(argp);
}

The generated code for the foo wrapper will then contain the snippets of the typemap with the special variables expanded. The rest of this chapter will need reading though to fully understand all of this, however, the relevant parts of the generated code for the above typemap can be seen below:

而後,爲 foo 包裝程序生成的代碼將包含帶有特殊變量擴展的類型映射的代碼段。本章的其他部分雖然須要閱讀才能徹底理解全部這些內容,可是,能夠在下面看到上述類型映射的生成代碼的相關部分:

SWIGINTERN PyObject *_wrap_foo(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
...
  void *argp1 = 0 ;
  int res1 = 0 ;
...
  res1 = SWIG_ConvertPtr(obj0, &argp1, SWIGTYPE_p_a_4__int, 0 |  0 );
  if (!SWIG_IsOK(res1)) {
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "foo" "', argument "
                       "1"" of type '" "int [10][4]""'");
  }
  arg1 = (int (*)[4])(argp1);
...
}

Searches for multi-argument typemaps are not mentioned unless a matching multi-argument typemap does actually exist. For example, the output for the code in the earlier multi-arguments section is as follows:

除非確實存在匹配的多參數類型映射,不然不涉及搜索多參數類型映射。例如,較早的多參數章節中的代碼輸出以下:

...
example.h:39: Searching for a suitable 'in' typemap for: char *buffer
  Looking for: char *buffer
  Multi-argument typemap found...
  Using: %typemap(in) (char *buffer, int len)
...

The second option for debugging is -debug-tmused and this displays the typemaps used. This option is a less verbose version of the -debug-tmsearch option as it only displays each successfully found typemap on a separate single line. The output displays the type, and name if present, the typemap method in brackets and then the actual typemap used in the same simplified format output by the -debug-tmsearchoption. Below is the output for the example code at the start of this section on debugging.

調試的第二個選項是 -debug-tmused,它顯示了使用的類型映射。這個選項是 -debug-tmsearch 選項的一個不太冗長的版本,由於它只在單獨的一行上顯示每一個成功找到的類型映射。輸出將顯示類型和名稱(若是存在),括號中的類型映射方法,而後顯示由 -debug-tmsearch 選項以相同簡化格式輸出的實際類型映射。如下是本節開始時有關調試的示例代碼的輸出。

$ swig -perl -debug-tmused example.i
example.h:3: Typemap for Row4 rows[10] (in) : %typemap(in) SWIGTYPE []
example.h:3: Typemap for Row4 rows[10] (typecheck) : %typemap(typecheck) SWIGTYPE *
example.h:3: Typemap for Row4 rows[10] (freearg) : %typemap(freearg) SWIGTYPE []
example.h:3: Typemap for void foo (out) : %typemap(out) void

Now, consider the following interface file:

如今,考慮下面的接口文件:

%module example

%{
void set_value(const char* val) {}
%}

%typemap(check) char *NON_NULL {
  if (!$1) {
    /* ... error handling ... */
  }
}

// use default pointer handling instead of strings
%apply SWIGTYPE * { const char* val, const char* another_value }

%typemap(check) const char* val = char* NON_NULL;

%typemap(arginit, noblock=1) const char* val {
  $1 = "";
}

void set_value(const char* val);

and the output debug:

輸出調試結果:

swig -perl5 -debug-tmused example.i
example.i:21: Typemap for char const *val (arginit) : %typemap(arginit) char const *val
example.i:21: Typemap for char const *val (in) : %apply SWIGTYPE * { char const *val }
example.i:21: Typemap for char const *val (typecheck) : %apply SWIGTYPE * { char const *val }
example.i:21: Typemap for char const *val (check) : %typemap(check) char const *val = char *NON_NULL
example.i:21: Typemap for char const *val (freearg) : %apply SWIGTYPE * { char const *val }
example.i:21: Typemap for void set_value (out) : %typemap(out) void

The following observations about what is displayed can be noted (the same applies for -debug-tmsearch):

  • The relevant typemap is shown, but for typemap copying, the appropriate %typemap or %apply is displayed, for example, the "check" and "in" typemaps.
  • The typemap modifiers are not shown, eg the noblock=1 modifier in the "arginit" typemap.
  • The exact %apply statement might look different to what is in the actual code. For example, the const char* another_value is not shown as it is not relevant here. Also the types may be displayed slightly differently - char const * and not const char*.

能夠注意到如下有關顯示內容的觀察結果(-debug-tmsearch 一樣適用):

  • 顯示了相關的類型映射,可是對於複製類型映射,將顯示適當的 %typemap%apply,例如,checkin 類型映射。
  • 類型映射修飾符未顯示,例如,arginit 類型映射中的 noblock = 1 修飾符。
  • 確切的 %apply 語句可能看起來與實際代碼不一樣。例如,未顯示 const char* another_value,由於此處不相關。一樣,類型的顯示可能略有不一樣——char const* 而不是 const char*

11.4 代碼生成規則

This section describes rules by which typemap code is inserted into the generated wrapper code.

本節描述將類型映射代碼插入到生成的包裝器代碼中的規則。

11.4.1 做用域

When a typemap is defined like this:

當類型映射定義以下:

%typemap(in) int {
  $1 = PyInt_AsLong($input);
}

the typemap code is inserted into the wrapper function using a new block scope. In other words, the wrapper code will look like this:

使用新的塊做用域將類型映射代碼插入包裝器函數。換句話說,包裝器代碼將以下所示:

wrap_whatever() {
  ...
  // Typemap code
  {
    arg1 = PyInt_AsLong(obj1);
  }
  ...
}

Because the typemap code is enclosed in its own block, it is legal to declare temporary variables for use during typemap execution. For example:

由於類型映射代碼包含在其本身的塊中,因此聲明臨時變量供在類型映射執行期間使用是合法的。例如:

%typemap(in) short {
  long temp;          /* Temporary value */
  if (Tcl_GetLongFromObj(interp, $input, &temp) != TCL_OK) {
    return TCL_ERROR;
  }
  $1 = (short) temp;
}

Of course, any variables that you declare inside a typemap are destroyed as soon as the typemap code has executed (they are not visible to other parts of the wrapper function or other typemaps that might use the same variable names).

Occasionally, typemap code will be specified using a few alternative forms. For example:

固然,你在類型映射中聲明的任何變量都將在該類型映射代碼執行後當即銷燬(它們對於包裝器函數的其餘部分,或其餘可能使用相同變量名的類型映射不可見)。

有時,會使用一些其餘形式來指定類型映射代碼。例如:

%typemap(in) int "$1 = PyInt_AsLong($input);";
%typemap(in) int %{
$1 = PyInt_AsLong($input);
%}
%typemap(in, noblock=1) int {
$1 = PyInt_AsLong($input);
}

These three forms are mainly used for cosmetics--the specified code is not enclosed inside a block scope when it is emitted. This sometimes results in a less complicated looking wrapper function. Note that only the third of the three typemaps have the typemap code passed through the SWIG preprocessor.

這三種形式主要用於化妝——發出特定代碼時,指定代碼未包含在塊做用域內。有時這會致使看起來不太複雜的包裝器函數。請注意,三個類型映射中只有三分之一具備經過 SWIG 預處理程序傳遞的類型映射代碼。

11.4.2 聲明新的局部變量

Sometimes it is useful to declare a new local variable that exists within the scope of the entire wrapper function. A good example of this might be an application in which you wanted to marshal strings. Suppose you had a C++ function like this

有時,聲明存在於整個包裝器函數範圍內的新局部變量頗有用。一個很好的例子就是你想在其中傳遞字符串的應用程序。假設你有一個這樣的 C++ 函數

int foo(std::string *s);

and you wanted to pass a native string in the target language as an argument. For instance, in Perl, you wanted the function to work like this:

而且你想傳遞目標語言中的原生字符串做爲參數。例如,在 Perl 中,你但願函數像這樣工做:

$x = foo("Hello World");

To do this, you can't just pass a raw Perl string as the std::string * argument. Instead, you have to create a temporary std::string object, copy the Perl string data into it, and then pass a pointer to the object. To do this, simply specify the typemap with an extra parameter like this:

爲此,你不能僅將原始 Perl 字符串做爲 std::string * 參數傳遞。相反,你必須建立一個臨時的 std::string 對象,將 Perl 字符串數據複製到其中,而後將指針傳遞給該對象。爲此,只需使用以下額外參數指定類型映射:

%typemap(in) std::string * (std::string temp) {
  unsigned int len;
  char        *s;
  s = SvPV($input, len);         /* Extract string data */
  temp.assign(s, len);           /* Assign to temp */
  $1 = &temp;                   /* Set argument to point to temp */
}

In this case, temp becomes a local variable in the scope of the entire wrapper function. For example:

在這種狀況下,temp 成爲整個包裝器函數範圍內的局部變量。例如:

wrap_foo() {
  std::string temp;   // <--- Declaration of temp goes here
  ...

  /* Typemap code */
  {
    ...
    temp.assign(s, len);
    ...
  }
  ...
}

When you set temp to a value, it persists for the duration of the wrapper function and gets cleaned up automatically on exit.

It is perfectly safe to use more than one typemap involving local variables in the same declaration. For example, you could declare a function as :

當你將 temp 設置爲一個值時,它將在包裝器函數的整個過程當中持續存在,並在退出時自動清除。

在同一聲明中使用多個涉及局部變量的類型映射是絕對安全的。例如,你能夠將一個函數聲明爲:

void foo(std::string *x, std::string *y, std::string *z);

This is safely handled because SWIG actually renames all local variable references by appending an argument number suffix. Therefore, the generated code would actually look like this:

這是安全處理的,由於 SWIG 實際上經過附加參數編號後綴來重命名全部局部變量引用。所以,生成的代碼實際上將以下所示:

wrap_foo() {
  int *arg1;    /* Actual arguments */
  int *arg2;
  int *arg3;
  std::string temp1;    /* Locals declared in the typemap */
  std::string temp2;
  std::string temp3;
  ...
  {
    char *s;
    unsigned int len;
    ...
    temp1.assign(s, len);
    arg1 = *temp1;
  }
  {
    char *s;
    unsigned int len;
    ...
    temp2.assign(s, len);
    arg2 = &temp2;
  }
  {
    char *s;
    unsigned int len;
    ...
    temp3.assign(s, len);
    arg3 = &temp3;
  }
  ...
}

There is an exception: if the variable name starts with the _global_ prefix, the argument number is not appended. Such variables can be used throughout the generated wrapper function. For example, the above typemap could be rewritten to use _global_temp instead of temp and the generated code would then contain a single _global_temp variable instead of temp1, temp2 and temp3:

有一個例外:若是變量名以 _global_ 前綴開頭,則不附加參數編號。此類變量可在整個生成的包裝器函數中使用。例如,上面的類型映射能夠重寫爲使用 _global_temp 而不是 temp,而後生成的代碼將包含單個 _global_temp 變量而不是 temp1temp2temp3

%typemap(in) std::string * (std::string _global_temp) {
 ... as above ...
}

Some typemaps do not recognize local variables (or they may simply not apply). At this time, only typemaps that apply to argument conversion support this (input typemaps such as the "in" typemap).

Note:

When declaring a typemap for multiple types, each type must have its own local variable declaration.

一些類型映射不能識別局部變量(或者它們可能根本不適用)。目前,僅適用於參數轉換的類型映射支持此功能(輸入類型映射,例如 in 類型映射)。

注意

當聲明多個類型的類型映射時,每一個類型必須具備本身的局部變量聲明。

%typemap(in) const std::string *, std::string * (std::string temp) // NO!
// only std::string * has a local variable
// const std::string * does not (oops)
....

%typemap(in) const std::string * (std::string temp), std::string * (std::string temp) // Correct
....

11.4.3 特殊變量

Within all typemaps, the following special variables are expanded. This is by no means a complete list as some target languages have additional special variables which are documented in the language specific chapters.

下列特殊變量是對全部類型映射的擴展。這毫不是一個完整的列表,由於某些目標語言具備額外的特殊變量,這些特殊變量記錄在目標語言的特定章節中。

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

Within the table, $n refers to a specific type within the typemap specification. For example, if you write this

在表中,$n 表示類型映射規範中的特定類型。例如,若是你編寫此

%typemap(in) int *INPUT {

}

then $1 refers to int *INPUT. If you have a typemap like this,

那麼 $1 指向 int *INPUT。若是你有以下類型映射,

%typemap(in) (int argc, char *argv[]) {
  ...
}

then $1 refers to int argc and $2 refers to char *argv[].

Substitutions related to types and names always fill in values from the actual code that was matched. This is useful when a typemap might match multiple C datatype. For example:

那麼 $1 指向 int argc$2 指向 char *argv[]

與類型和名稱相關的替換老是填充匹配的實際代碼中的值。當一個類型映射可能匹配多個 C 數據類型時頗有用。例如:

%typemap(in)  int, short, long {
  $1 = ($1_ltype) PyInt_AsLong($input);
}

In this case, $1_ltype is replaced with the datatype that is actually matched.

When typemap code is emitted, the C/C++ datatype of the special variables $1 and $2 is always an "ltype." An "ltype" is simply a type that can legally appear on the left-hand side of a C assignment operation. Here are a few examples of types and ltypes:

在這種狀況下,將 $1_ltype 替換爲實際匹配的數據類型。

當發出類型映射代碼時,特殊變量 $1$2 的 C/C++ 數據類型始終是 ltypeltype 只是能夠合法出如今 C 賦值操做左側的類型。如下是一些類型和 ltypes 的示例:

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

In most cases a ltype is simply the C datatype with qualifiers stripped off. In addition, arrays are converted into pointers.

Variables such as $&1_type and $*1_type are used to safely modify the type by removing or adding pointers. Although not needed in most typemaps, these substitutions are sometimes needed to properly work with typemaps that convert values between pointers and values.

If necessary, type related substitutions can also be used when declaring locals. For example:

在大多數狀況下,ltype 只是帶有限定符的 C 數據類型。另外,數組被轉換爲指針。

諸如 $&1_type$*1_type 之類的變量用於經過刪除或添加指針來安全地修改類型。儘管在大多數類型映射中不須要,可是有時有時須要這些替換才能正確處理在指針和值之間轉換值的類型映射。

若有必要,在聲明局部變量時也可使用類型相關的替換。例如:

%typemap(in) int * ($*1_type temp) {
  temp = PyInt_AsLong($input);
  $1 = &temp;
}

There is one word of caution about declaring local variables in this manner. If you declare a local variable using a type substitution such as $1_ltype temp, it won't work like you expect for arrays and certain kinds of pointers. For example, if you wrote this,

以這種方式聲明局部變量有一個警告。若是你使用諸如 $1_ltype temp 之類的類型替換聲明局部變量,它將沒法像你指望的那樣使用數組和某些類型的指針。例如,若是你編寫了此代碼,

%typemap(in) int [10][20] {
  $1_ltype temp;
}

then the declaration of temp will be expanded as

那麼 temp 的聲明將被擴展爲

int (*)[20] temp;

This is illegal C syntax and won't compile. There is currently no straightforward way to work around this problem in SWIG due to the way that typemap code is expanded and processed. However, one possible workaround is to simply pick an alternative type such as void * and use casts to get the correct type when needed. For example:

這是非法的 C 語法,不會被編譯。因爲類型映射代碼的擴展和處理方式,當前在 SWIG 中沒有解決此問題的簡單方法。然而,一種可能的解決方法是簡單地選擇一種替代類型,例如 void *,並在須要時使用強制類型轉換來獲取正確的類型。例如:

%typemap(in) int [10][20] {
  void *temp;
  ...
  (($1_ltype) temp)[i][j] = x;    /* set a value */
  ...
}

Another approach, which only works for arrays is to use the $1_basetype substitution. For example:

另外一種只對數組有效的方法是使用 $1_basetype 替換。例如:

%typemap(in) int [10][20] {
  $1_basetype temp[10][20];
  ...
  temp[i][j] = x;    /* set a value */
  ...
}

11.4.4 特殊變量宏

Special variable macros are like macro functions in that they take one or more input arguments which are used for the macro expansion. They look like macro/function calls but use the special variable $ prefix to the macro name. Note that unlike normal macros, the expansion is not done by the preprocessor, it is done during the SWIG parsing/compilation stages. The following special variable macros are available across all language modules.

特殊變量宏就像宏函數同樣,它們接受一個或多個用於宏擴展的輸入參數。它們看起來像是宏或函數調用,可是在宏名稱中使用特殊變量 $ 前綴。請注意,與普通宏不一樣,擴展不是由預處理器完成的,而是在 SWIG 解析或編譯階段完成的。如下特殊變量宏可在全部語言模塊中使用。

11.4.4.1 $descriptor(type)

This macro expands into the type descriptor structure for any C/C++ type specified in type. It behaves like the $1_descriptor special variable described above except that the type to expand is taken from the macro argument rather than inferred from the typemap type. For example, $descriptor(std::vector<int> *) will expand into SWIGTYPE_p_std__vectorT_int_t. This macro is mostly used in the scripting target languages and is demonstrated later in the Run-time type checker usage section.

這個宏擴展爲 type 中指定的任何 C/C++ 類型的類型描述符結構。它的行爲相似於上述的 $1_descriptor 特殊變量,不一樣之處在於要擴展的類型是從宏參數中獲取的,而不是從類型映射類型中推斷出來的。例如,$descriptor(std::vector<int> *) 將擴展爲 SWIGTYPE_p_std__vectorT_int_t。該宏主要用於腳本化目標語言,稍後在運行時類型檢查器用法章節中進行演示。

11.4.4.2 $typemap(method, typepattern)

This macro uses the pattern matching rules described earlier to lookup and then substitute the special variable macro with the code in the matched typemap. The typemap to search for is specified by the arguments, where method is the typemap method name and typepattern is a type pattern as per the %typemap specification in the Defining a typemap section.

The special variables within the matched typemap are expanded into those for the matched typemap type, not the typemap within which the macro is called. In practice, there is little use for this macro in the scripting target languages. It is mostly used in the target languages that are statically typed as a way to obtain the target language type given the C/C++ type and more commonly only when the C++ type is a template parameter.

The example below is for C# only and uses some typemap method names documented in the C# chapter, but it shows some of the possible syntax variations.

此宏使用前面描述的模式匹配規則查找,而後用匹配的類型映射中的代碼替換特殊變量宏。根據參數指定要搜索的類型映射,其中 method 是類型映射方法名稱,而 typepattern 是類型模式,正如定義類型映射章節的 %typemap 規則。

匹配的類型映射中的特殊變量將擴展爲匹配的類型映射類型的特殊變量,而不是其中調用宏的類型映射。實際上,在腳本目標語言中,此宏不多使用。它一般用在目標語言中,這些目標語言是靜態類型化的,以便在給定 C/C++ 類型的狀況下獲取目標語言類型,而且更常見的狀況是僅在 C++ 類型是模板參數時使用。

下面的示例僅適用於 C#,並使用了 C# 一章中記錄的某些類型映射方法名稱,但它顯示了一些可能的語法變體。

%typemap(cstype) unsigned long    "uint"
%typemap(cstype) unsigned long bb "bool"
%typemap(cscode) BarClass %{
  void foo($typemap(cstype, unsigned long aa) var1,
           $typemap(cstype, unsigned long bb) var2,
           $typemap(cstype, (unsigned long bb)) var3,
           $typemap(cstype, unsigned long) var4)
  {
    // do something
  }
%}

The result is the following expansion

結果是下列擴展

%typemap(cstype) unsigned long    "uint"
%typemap(cstype) unsigned long bb "bool"
%typemap(cscode) BarClass %{
  void foo(uint var1,
           bool var2,
           bool var3,
           uint var4)
  {
    // do something
  }
%}

11.4.5 特殊變量與類型映射屬性

As of SWIG-3.0.7 typemap attributes will also expand special variables and special variable macros.

Example usage showing the expansion in the 'out' attribute (C# specific) as well as the main typemap body:

從 SWIG-3.0.7 開始,類型映射屬性還將擴展特殊變量和特殊變量宏。

用法示例顯示 out 屬性(特定於 C#)以及主要的類型映射主體中的擴展:

%typemap(ctype, out="$*1_ltype") unsigned int& "$*1_ltype"

is equivalent to the following as $*1_ltype expands to unsigned int:

與下面的等價,至關於 $*1_ltype 擴展 unsigned int

%typemap(ctype, out="unsigned int") unsigned int& "unsigned int"

11.4.6 特殊變量聯合特殊變量宏

Special variables can also be used within special variable macros. The special variables are expanded before they are used in the special variable macros.

Consider the following C# typemaps:

特殊變量也能夠在特殊變量宏中使用。在特殊變量宏中使用特殊變量以前,先對其進行擴展。

考慮如下 C# 類型映射:

%typemap(cstype) unsigned int "uint"
%typemap(cstype, out="$typemap(cstype, $*1_ltype)") unsigned int& "$typemap(cstype, $*1_ltype)"

Special variables are expanded first and hence the above is equivalent to:

特殊變量首先被擴展,所以以上等效於:

%typemap(cstype) unsigned int "uint"
%typemap(cstype, out="$typemap(cstype, unsigned int)") unsigned int& "$typemap(cstype, unsigned int)"

which then expands to:

而後擴展:

%typemap(cstype) unsigned int "uint"
%typemap(cstype, out="uint") unsigned int& "uint"

11.5 通用類型映射方法

The set of typemaps recognized by a language module may vary. However, the following typemap methods are nearly universal:

語言模塊識別的類型映射集可能有所不一樣。可是,如下類型映射方法幾乎是通用的:

11.5.1 in 類型映射

The "in" typemap is used to convert function arguments from the target language to C. For example:

in 類型映射用於將函數參數從目標語言轉換爲 C 語言。例如:

%typemap(in) int {
  $1 = PyInt_AsLong($input);
}

The following special variables are available:

如下特殊變量可用:

$input            - Input object holding value to be converted.
$symname          - Name of function/method being wrapped

This is probably the most commonly redefined typemap because it can be used to implement customized conversions.

In addition, the "in" typemap allows the number of converted arguments to be specified. The numinputs attributes facilitates this. For example:

這多是最多見的從新定義的類型映射,由於它可用於實現自定義轉換。

另外,in 類型映射容許指定轉換參數的數量。numinputs 屬性有助於實現這一點。例如:

// Ignored argument.
%typemap(in, numinputs=0) int *out (int temp) {
  $1 = &temp;
}

At this time, only zero or one arguments may be converted. When numinputs is set to 0, the argument is effectively ignored and cannot be supplied from the target language. The argument is still required when making the C/C++ call and the above typemap shows the value used is instead obtained from a locally declared variable called temp. Usually numinputs is not specified, whereupon the default value is 1, that is, there is a one to one mapping of the number of arguments when used from the target language to the C/C++ call. Multi-argument typemaps provide a similar concept where the number of arguments mapped from the target language to C/C++ can be changed for multiple adjacent C/C++ arguments.

Compatibility note: Specifying numinputs=0 is the same as the old "ignore" typemap.

此時,只能轉換零個或一個參數。當 numinputs 設置爲 0 時,該參數將被有效忽略,而且沒法從目標語言中提供。進行 C/C++ 調用時,仍然須要該參數,而且上面的類型映射顯示所使用的值是從局部聲明的名爲 temp 的變量中獲取的。一般不指定 numinputs,所以默認值爲 1,即從目標語言到 C/C++ 調用使用時,參數數量是一對一的映射。多參數類型映射提供了相似的概念,其中能夠爲多個相鄰的 C更改從目標語言映射到 C/C++ 的參數數量 / C++ 參數。

注意兼容性:指定 numinputs = 0 與舊的 ignore 類型映射相同。

11.5.2 typecheck 類型映射

The "typecheck" typemap is used to support overloaded functions and methods. It merely checks an argument to see whether or not it matches a specific type. For example:

typecheck 類型映射用於支持重載的函數和方法。它僅檢查參數以查看其是否與特定類型匹配。例如:

%typemap(typecheck, precedence=SWIG_TYPECHECK_INTEGER) int {
  $1 = PyInt_Check($input) ? 1 : 0;
}

For typechecking, the $1 variable is always a simple integer that is set to 1 or 0 depending on whether or not the input argument is the correct type. Set to 1 if the input argument is the correct type otherwise set to 0.

If you define new "in" typemaps and your program uses overloaded methods, you should also define a collection of "typecheck" typemaps. More details about this follow in the Typemaps and overloading section.

對於類型檢查,$1 變量始終是一個簡單整數,根據輸入參數是否爲正確的類型將其設置爲 10。若是輸入參數是正確的類型,則設置爲 1,不然設置爲 0

若是你定義新的 in 類型映射,而且你的程序使用重載方法,則還應該定義 typecheck 類型映射的集合。有關此問題的更多詳細信息,請參見類型映射與重載章節。

11.5.3 out 類型映射

The "out" typemap is used to convert function/method return values from C into the target language. For example:

out 類型映射用於將函數或方法的返回值從 C 轉換爲目標語言。例如:

%typemap(out) int {
  $result = PyInt_FromLong($1);
}

The following special variables are available.

如下特殊變量可用:

$result           - Result object returned to target language.
$symname          - Name of function/method being wrapped

The "out" typemap supports an optional attribute flag called "optimal". This is for code optimisation and is detailed in the Optimal code generation when returning by value section.

out 類型映射支持名爲 optimal 的可選屬性標誌。這是用於代碼優化的,在按值返回時的最佳代碼生成章節中進行了詳細說明。

11.5.4 arginit 類型映射

The "arginit" typemap is used to set the initial value of a function argument--before any conversion has occurred. This is not normally necessary, but might be useful in highly specialized applications. For example:

在進行任何轉換以前,arginit 類型映射用於設置函數參數的初始值。一般這不是必需的,但在高度專業化的應用程序中可能頗有用。例如:

// Set argument to NULL before any conversion occurs
%typemap(arginit) int *data {
  $1 = NULL;
}

11.5.5 default 類型映射

The "default" typemap is used to turn an argument into a default argument. For example:

default 類型映射用於將參數轉換爲默認參數。例如:

%typemap(default) int flags {
  $1 = DEFAULT_FLAGS;
}
...
int foo(int x, int y, int flags);

The primary use of this typemap is to either change the wrapping of default arguments or specify a default argument in a language where they aren't supported (like C). Target languages that do not support optional arguments, such as Java and C#, effectively ignore the value specified by this typemap as all arguments must be given.

Once a default typemap has been applied to an argument, all arguments that follow must have default values. See the Default/optional arguments section for further information on default argument wrapping.

此類型映射的主要用途是更改默認參數的包裝,或爲不支持默認參數的語言(例如 C)指定默認參數。不支持可選參數的目標語言(例如 Java 和 C#)實際上會忽略此類型映射所指定的值,由於必須提供全部參數。

將默認類型映射應用於參數後,後面的全部參數都必須具備默認值。有關默認參數包裝的更多信息,請參見默認與可選參數章節。

11.5.6 check 類型映射

The "check" typemap is used to supply value checking code during argument conversion. The typemap is applied after arguments have been converted. For example:

check 類型映射用於在參數轉換期間提供值檢查代碼。類型參數是在參數轉換以後應用的。例如:

%typemap(check) int positive {
  if ($1 <= 0) {
    SWIG_exception(SWIG_ValueError, "Expected positive value.");
  }
}

11.5.7 argout 類型映射

The "argout" typemap is used to return values from arguments. This is most commonly used to write wrappers for C/C++ functions that need to return multiple values. The "argout" typemap is almost always combined with an "in" typemap---possibly to ignore the input value. For example:

argout 類型映射用於從參數返回值。這最經常使用於爲須要返回多個值的 C/C++ 函數編寫包裝器。argout 類型映射幾乎老是與 in 類型映射結合使用——可能會忽略輸入值。例如:

/* Set the input argument to point to a temporary variable */
%typemap(in, numinputs=0) int *out (int temp) {
  $1 = &temp;
}

%typemap(argout) int *out {
  // Append output value $1 to $result
  ...
}

The following special variables are available.

可使用下列特殊變量。

$result           - Result object returned to target language.
$input            - The original input object passed.
$symname          - Name of function/method being wrapped

The code supplied to the "argout" typemap is always placed after the "out" typemap. If multiple return values are used, the extra return values are often appended to return value of the function.

See the typemaps.i library file for examples.

提供給 argout 類型映射的代碼始終放置在 out 類型映射以後。若是使用多個返回值,則一般會將多餘的返回值附加到函數的返回值上。

有關示例,請參見 typemaps.i 庫文件。

11.5.8 freearg 類型映射

The "freearg" typemap is used to cleanup argument data. It is only used when an argument might have allocated resources that need to be cleaned up when the wrapper function exits. The "freearg" typemap usually cleans up argument resources allocated by the "in" typemap. For example:

freearg 類型映射用於清除參數數據。僅當參數可能分配了包裝器函數退出時須要清除的資源時才使用它。一般,freearg 類型映射會清除 in 類型映射分配的參數資源。例如:

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

The "freearg" typemap inserted at the end of the wrapper function, just before control is returned back to the target language. This code is also placed into a special variable $cleanup that may be used in other typemaps whenever a wrapper function needs to abort prematurely.

在控件返回到目標語言以前,將 freearg 類型映射插入包裝器函數的末尾。這段代碼也被放入一個特殊的變量 $cleanup 中,只要包裝器函數須要提早停止,該變量就能夠在其餘類型映射中使用。

11.5.9 newfree 類型映射

The "newfree" typemap is used in conjunction with the %newobject directive and is used to deallocate memory used by the return result of a function. For example:

newfree 類型映射與 %newobject 指令一塊兒使用,用於釋放函數返回結果使用的內存。例如:

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

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

See Object ownership and %newobject for further details.

更多細節請查看對象全部權和 %newobject章節。

11.5.10 ret 類型映射

The "ret" typemap is not used very often, but can be useful for anything associated with the return type, such as resource management, return value error checking, etc. Usually this can all be done in the "out" typemap, but sometimes it is handy to use the "out" typemap code untouched and add to the generated code using the code in the "ret" typemap. One such case is memory clean up. For example, a stringheap_t type is defined indicating that the returned memory must be deleted and a string_t type is defined indicating that the returned memory must not be deleted.

ret 類型映射不是很常用,可是對於與返回類型相關的任何事情(例如資源管理,返回值錯誤檢查等)都頗有用。一般均可以在 out 類型映射中完成,可是有時方便地使用未修改的 out 類型映射代碼,並使用 ret 類型映射中的代碼添加到生成的代碼中。一種這樣的狀況是內存清理。例如,定義了 stringheap_t 類型,指示必須刪除返回的內存,定義 string_t 類型,指示必須刪除返回的內存。

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

typedef char * string_t;
typedef char * stringheap_t;

string_t MakeString1();
stringheap_t MakeString2();

The "ret" typemap above will only be used for MakeString2, but both functions will use the default "out" typemap for char * provided by SWIG. The code above would ensure the appropriate memory is freed in all target languages as the need to provide custom "out" typemaps (which involve target language specific code) is not necessary.

This approach is an alternative to using the "newfree" typemap and %newobject as there is no need to list all the functions that require the memory cleanup, it is purely done on types.

上面的 ret 類型映射將僅用於 MakeString2,可是兩個函數都將使用 SWIG 提供的 char * 的默認out 類型映射。上面的代碼將確保在全部目標語言中釋放適當的內存,由於不須要提供自定義的 out 類型映射(涉及目標語言特定的代碼)。

這種方法是使用 newfree 類型映射和 %newobject 的一種替代方法,由於不須要列出全部須要內存清理的功能,它徹底是在類型上完成的。

11.5.11 memberin 類型映射

The "memberin" typemap is used to copy data from an already converted input value into a structure member. It is typically used to handle array members and other special cases. For example:

memberin 類型映射用於將數據從已經轉換的輸入值複製到結構體成員中。它一般用於處理數組成員和其餘特殊狀況。例如:

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

It is rarely necessary to write "memberin" typemaps---SWIG already provides a default implementation for arrays, strings, and other objects.

幾乎沒有必要編寫 memberin 類型映射——SWIG 已經爲數組、字符串和其餘對象提供了默認實現。

11.5.12 varin 類型映射

The "varin" typemap is used to convert objects in the target language to C for the purposes of assigning to a C/C++ global variable. This is implementation specific.

varin 類型映射用於將目標語言中的對象轉換爲 C,以分配給 C/C++ 全局變量。這是特定於實現的。

11.5.13 varout 類型映射

The "varout" typemap is used to convert a C/C++ object to an object in the target language when reading a C/C++ global variable. This is implementation specific.

讀取 C/C++ 全局變量時,varout 類型映射用於將 C/C++ 對象轉換爲目標語言中的對象。這是特定於實現的。

11.5.14 throws 類型映射

The "throws" typemap is only used when SWIG parses a C++ method with an exception specification or has the %catches feature attached to the method. It provides a default mechanism for handling C++ methods that have declared the exceptions they will throw. The purpose of this typemap is to convert a C++ exception into an error or exception in the target language. It is slightly different to the other typemaps as it is based around the exception type rather than the type of a parameter or variable. For example:

僅當 SWIG 解析具備異常規範的 C++ 方法,或將 %catches 功能附加到該方法時才使用 throw 類型映射。它提供了一種默認機制來處理聲明瞭將要拋出的異常的 C++ 方法。此類型映射的目的是將 C++ 異常轉換爲目標語言中的錯誤或異常。它與其餘類型映射略有不一樣,由於它基於異常類型而不是參數或變量的類型。例如:

%typemap(throws) const char * %{
  PyErr_SetString(PyExc_RuntimeError, $1);
  SWIG_fail;
%}
void bar() throw (const char *);

As can be seen from the generated code below, SWIG generates an exception handler with the catch block comprising the "throws" typemap content.

從下面的生成代碼中能夠看出,SWIG 生成帶有 catch 塊的異常處理程序,該 catch 塊包含 throw 類型映射內容。

...
try {
  bar();
}
catch(char const *_e) {
  PyErr_SetString(PyExc_RuntimeError, _e);
  SWIG_fail;
}
...

Note that if your methods do not have an exception specification yet they do throw exceptions, SWIG cannot know how to deal with them. For a neat way to handle these, see the Exception handling with %exception section.

請注意,若是你的方法沒有異常規範,但它們確實會引起異常,則 SWIG 沒法知道如何處理它們。有關處理這些錯誤的巧妙方法,請參閱使用 %exception 處理異常章節。

11.6 一些類型映射示例

This section contains a few examples. Consult language module documentation for more examples.

本節包含一些示例。有關更多示例,請查閱語言模塊的文檔。

11.6.1 數組的類型映射

A common use of typemaps is to provide support for C arrays appearing both as arguments to functions and as structure members.

For example, suppose you had a function like this:

類型映射的一種常見用法是爲 C 數組提供支持,這些 C 數組既做爲函數的參數出現,又做爲結構體成員出現。

例如,假設你具備以下函數:

void set_vector(int type, float value[4]);

If you wanted to handle float value[4] as a list of floats, you might write a typemap similar to this:

若是你想將 float value[4] 做爲一列浮點數表處理,則能夠編寫相似於如下內容的類型映射:

%typemap(in) float value[4] (float temp[4]) {
  int i;
  if (!PySequence_Check($input)) {
    PyErr_SetString(PyExc_ValueError, "Expected a sequence");
    SWIG_fail;
  }
  if (PySequence_Length($input) != 4) {
    PyErr_SetString(PyExc_ValueError, "Size mismatch. Expected 4 elements");
    SWIG_fail;
  }
  for (i = 0; i < 4; i++) {
    PyObject *o = PySequence_GetItem($input, i);
    if (PyNumber_Check(o)) {
      temp[i] = (float) PyFloat_AsDouble(o);
    } else {
      PyErr_SetString(PyExc_ValueError, "Sequence elements must be numbers");
      SWIG_fail;
    }
  }
  $1 = temp;
}

In this example, the variable temp allocates a small array on the C stack. The typemap then populates this array and passes it to the underlying C function.

When used from Python, the typemap allows the following type of function call:

在這個例子中,變量 temp 在 C 棧上分配了一個小數組。而後,類型映射將填充此數組,並將其傳遞給基礎 C 函數。

當從 Python 使用時,類型映射容許如下類型的函數調用:

>>> set_vector(type, [ 1, 2.5, 5, 20 ])

If you wanted to generalize the typemap to apply to arrays of all dimensions you might write this:

若是要泛化類型映射以應用於全部維度的數組,則能夠這樣編寫:

%typemap(in) float value[ANY] (float temp[$1_dim0]) {
  int i;
  if (!PySequence_Check($input)) {
    PyErr_SetString(PyExc_ValueError, "Expected a sequence");
    SWIG_fail;
  }
  if (PySequence_Length($input) != $1_dim0) {
    PyErr_SetString(PyExc_ValueError, "Size mismatch. Expected $1_dim0 elements");
    SWIG_fail;
  }
  for (i = 0; i < $1_dim0; i++) {
    PyObject *o = PySequence_GetItem($input, i);
    if (PyNumber_Check(o)) {
      temp[i] = (float) PyFloat_AsDouble(o);
    } else {
      PyErr_SetString(PyExc_ValueError, "Sequence elements must be numbers");
      SWIG_fail;
    }
  }
  $1 = temp;
}

In this example, the special variable $1_dim0 is expanded with the actual array dimensions. Multidimensional arrays can be matched in a similar manner. For example:

在這個例子中,特殊變量 $1_dim0 被擴展爲實際的數組維度。多維數組能夠相似的方式進行匹配。例如:

%typemap(in) float matrix[ANY][ANY] (float temp[$1_dim0][$1_dim1]) {
  ... convert a 2d array ...
}

For large arrays, it may be impractical to allocate storage on the stack using a temporary variable as shown. To work with heap allocated data, the following technique can be used.

對於大型數組,使用所示的臨時變量在堆棧上分配存儲可能不切實際。要使用堆分配的數據,可使用如下技術。

%typemap(in) float value[ANY] {
  int i;
  if (!PySequence_Check($input)) {
    PyErr_SetString(PyExc_ValueError, "Expected a sequence");
    SWIG_fail;
  }
  if (PySequence_Length($input) != $1_dim0) {
    PyErr_SetString(PyExc_ValueError, "Size mismatch. Expected $1_dim0 elements");
    SWIG_fail;
  }
  $1 = (float *) malloc($1_dim0*sizeof(float));
  for (i = 0; i < $1_dim0; i++) {
    PyObject *o = PySequence_GetItem($input, i);
    if (PyNumber_Check(o)) {
      $1[i] = (float) PyFloat_AsDouble(o);
    } else {
      free($1);
      PyErr_SetString(PyExc_ValueError, "Sequence elements must be numbers");
      SWIG_fail;
    }
  }
}
%typemap(freearg) float value[ANY] {
  if ($1) free($1);
}

In this case, an array is allocated using malloc. The freearg typemap is then used to release the argument after the function has been called.

Another common use of array typemaps is to provide support for array structure members. Due to subtle differences between pointers and arrays in C, you can't just "assign" to a array structure member. Instead, you have to explicitly copy elements into the array. For example, suppose you had a structure like this:

在這種狀況下,使用 malloc 分配數組。而後,在調用函數後,使用 freearg 類型映射釋放參數。

數組類型映射的另外一個常見用途是爲數組結構體成員提供支持。因爲 C 語言中的指針和數組之間存在細微的差別,所以你不能只是「分配」給數組結構體成員。相反,你必須將元素顯式複製到數組中。例如,假設你具備這樣的結構體:

struct SomeObject {
  float value[4];
  ...
};

When SWIG runs, it won't produce any code to set the vec member. You may even get a warning message like this:

SWIG 運行時,不會產生任何代碼來設置 vec 成員。你甚至可能收到如下警告消息:

$ swig -python  example.i
example.i:10: Warning 462: Unable to set variable of type float [4].

These warning messages indicate that SWIG does not know how you want to set the vec field.

To fix this, you can supply a special "memberin" typemap like this:

這些警告消息代表 SWIG 不知道你如何設置 vec 字段。

要解決此問題,能夠提供一個特殊的 memberin 類型映射,以下所示:

%typemap(memberin) float [ANY] {
  int i;
  for (i = 0; i < $1_dim0; i++) {
      $1[i] = $input[i];
  }
}

The memberin typemap is used to set a structure member from data that has already been converted from the target language to C. In this case, $input is the local variable in which converted input data is stored. This typemap then copies this data into the structure.

When combined with the earlier typemaps for arrays, the combination of the "in" and "memberin" typemap allows the following usage:

memberin 類型映射用於從已經從目標語言轉換爲 C 的數據中設置結構體成員。在這種狀況下,$input 是局部變量,用於存儲轉換後的輸入數據。而後,此類型映射將此數據複製到結構體中。

當與早期的數組類型映射結合使用時,inmemberin 類型映射的組合容許如下用法:

>>> s = SomeObject()
>>> s.x = [1, 2.5, 5, 10]

Related to structure member input, it may be desirable to return structure members as a new kind of object. For example, in this example, you will get very odd program behavior where the structure member can be set nicely, but reading the member simply returns a pointer:

與結構體成員輸入有關,可能但願將結構體成員做爲一種新的對象返回。例如,在此示例中,你將得到很是奇怪的程序行爲,能夠很好地設置結構體成員,可是讀取成員僅返回一個指針:

>>> s = SomeObject()
>>> s.x = [1, 2.5, 5, 10]
>>> print s.x
_1008fea8_p_float
>>>

To fix this, you can write an "out" typemap. For example:

要修正的話,你可使用 out 類型映射。例如:

%typemap(out) float [ANY] {
  int i;
  $result = PyList_New($1_dim0);
  for (i = 0; i < $1_dim0; i++) {
    PyObject *o = PyFloat_FromDouble((double) $1[i]);
    PyList_SetItem($result, i, o);
  }
}

Now, you will find that member access is quite nice:

如今,你能夠發現成員訪問變的至關正常:

>>> s = SomeObject()
>>> s.x = [1, 2.5, 5, 10]
>>> print s.x
[ 1, 2.5, 5, 10]

Compatibility Note: SWIG1.1 used to provide a special "memberout" typemap. However, it was mostly useless and has since been eliminated. To return structure members, simply use the "out" typemap.

注意兼容性:SWIG1.1 過去提供特殊的 memberout 類型映射。可是,它幾乎沒有用,所以已被淘汰。要返回結構體成員,只需使用 out 類型映射。

11.6.2 用類型映射的實現限制

One particularly interesting application of typemaps is the implementation of argument constraints. This can be done with the "check" typemap. When used, this allows you to provide code for checking the values of function arguments. For example:

類型映射的一個有趣應用是實現參數限制。這能夠用 check 類型映射作到。類型映射容許你提供代碼以檢查函數參數的值。例如:

%module math

%typemap(check) double posdouble {
  if ($1 < 0) {
    croak("Expecting a positive number");
  }
}

...
double sqrt(double posdouble);

This provides a sanity check to your wrapper function. If a negative number is passed to this function, a Perl exception will be raised and your program terminated with an error message.

This kind of checking can be particularly useful when working with pointers. For example:

這爲包裝器函數提供了完整性檢查。若是將負數傳遞給此函數,則會引起 Perl 異常,而且你的程序終止並顯示錯誤消息。

在使用指針時,這種檢查特別有用。例如:

%typemap(check) Vector * {
  if ($1 == 0) {
    PyErr_SetString(PyExc_TypeError, "NULL Pointer not allowed");
    SWIG_fail;
  }
}

will prevent any function involving a Vector * from accepting a NULL pointer. As a result, SWIG can often prevent a potential segmentation faults or other run-time problems by raising an exception rather than blindly passing values to the underlying C/C++ program.

會阻止任何涉及 Vector * 的函數接受空指針。最終,SWIG 一般能夠經過引起異常,而不是將值盲目地傳遞給底層 C/C++ 程序來防止潛在的分段錯誤或其餘運行時問題。

11.7 多目標語言的類型映射

The code within typemaps is usually language dependent, however, many target languages support the same typemaps. In order to distinguish typemaps across different languages, the preprocessor should be used. For example, the "in" typemap for Perl and Ruby could be written as:

類型映射中的代碼一般取決於語言,可是,許多目標語言都支持相同的類型映射。爲了區分不一樣語言之間的類型映射,應使用預處理器。例如,Perl 和 Ruby 的 in 類型映射能夠寫爲:

#if defined(SWIGPERL)
  %typemap(in) int "$1 = ($1_ltype) SvIV($input);"
#elif defined(SWIGRUBY)
  %typemap(in) int "$1 = NUM2INT($input);"
#else
  #warning no "in" typemap defined
#endif

The full set of language specific macros is defined in the Conditional Compilation section. The example above also shows a common approach of issuing a warning for an as yet unsupported language.

Compatibility note: In SWIG-1.1 different languages could be distinguished with the language name being put within the %typemap directive, for example, %typemap(ruby, in) int "$1 = NUM2INT($input);".

條件編譯章節中定義了特定於語言的完整宏集合。上面的示例還顯示了針對尚不支持的語言發出警告的常見方法。

注意兼容性:在 SWIG-1.1中,能夠經過在 %typemap 指令中放入語言名稱來區分不一樣的語言,例如,%typemap(ruby, in) int "$1 = NUM2INT($input);"

11.8 返回值時的最優代碼生成

The "out" typemap is the main typemap for return types. This typemap supports an optional attribute flag called "optimal", which is for reducing temporary variables and the amount of generated code, thereby giving the compiler the opportunity to use return value optimization for generating faster executing code. It only really makes a difference when returning objects by value and has some limitations on usage, as explained later on.

When a function returns an object by value, SWIG generates code that instantiates the default type on the stack then assigns the value returned by the function call to it. A copy of this object is then made on the heap and this is what is ultimately stored and used from the target language. This will be clearer considering an example. Consider running the following code through SWIG:

out 類型映射是返回類型的主要類型映射。此類型映射支持一個稱爲 optimal 的可選屬性標誌,該標誌用於減小臨時變量和所生成的代碼量,從而使編譯器有機會使用返回值優化來生成執行速度更快的代碼。如後面所述,只有在按值返回對象時,它才真正有所不一樣,而且在用法上有一些限制。

當函數按值返回對象時,SWIG 會生成代碼,該代碼實例化堆棧上的默認類型,而後將函數調用返回的值分配給它。而後在堆上建立此對象的副本,這是最終從目標語言存儲和使用的對象。考慮一個例子,這將更加清楚。考慮經過 SWIG 運行如下代碼:

%typemap(out) SWIGTYPE %{
  $result = new $1_ltype((const $1_ltype &)$1);
%}

%inline %{
#include <iostream>
using namespace std;

struct XX {
  XX() { cout << "XX()" << endl; }
  XX(int i) { cout << "XX(" << i << ")" << endl; }
  XX(const XX &other) { cout << "XX(const XX &)" << endl; }
  XX & operator =(const XX &other) { cout << "operator=(const XX &)" << endl; return *this; }
  ~XX() { cout << "~XX()" << endl; }
  static XX create() {
    return XX(0);
  }
};
%}

The "out" typemap shown is the default typemap for C# when returning objects by value. When making a call to XX::create() from C#, the output is as follows:

當按值返回對象時,顯示的 out 類型映射是 C# 的默認類型映射。從 C# 調用 XX::create() 時,輸出以下:

XX()
XX(0)
operator=(const XX &)
~XX()
XX(const XX &)
~XX()
~XX()

Note that three objects are being created as well as an assignment. Wouldn't it be great if the XX::create() method was the only time a constructor was called? As the method returns by value, this is asking a lot and the code that SWIG generates by default makes it impossible for the compiler to use return value optimisation (RVO). However, this is where the "optimal" attribute in the "out" typemap can help out. If the typemap code is kept the same and just the "optimal" attribute specified like this:

請注意,正在建立三個對象以及一個分配。若是惟一調用構造函數的方法是 XX::create() 方法,那不是很好嗎?因爲該方法按值返回,所以要求不少,而 SWIG 默認生成的代碼使編譯器沒法使用返回值優化(RVO)。可是,這是 out 類型映射中的 optimal 屬性能夠提供幫助的地方。若是類型映射代碼保持相同,而且僅指定 optimal 屬性,以下所示:

%typemap(out, optimal="1") SWIGTYPE %{
  $result = new $1_ltype((const $1_ltype &)$1);
%}

then when the code is run again, the output is simply:

再次運行代碼,輸出很簡單:

XX(0)
~XX()

How the "optimal" attribute works is best explained using the generated code. Without "optimal", the generated code is:

使用生成的代碼能夠最好地解釋 optimal 屬性的工做方式。若是沒有 optimal,則生成的代碼爲:

SWIGEXPORT void * SWIGSTDCALL CSharp_XX_create() {
  void * jresult ;
  XX result;
  result = XX::create();
  jresult = new XX((const XX &)result);
  return jresult;
}

With the "optimal" attribute, the code is:

有了 optimal 屬性,代碼爲:

SWIGEXPORT void * SWIGSTDCALL CSharp_XX_create() {
  void * jresult ;
  jresult = new XX((const XX &)XX::create());
  return jresult;
}

The major difference is the result temporary variable holding the value returned from XX::create() is no longer generated and instead the copy constructor call is made directly from the value returned by XX::create(). With modern compilers implementing RVO, the copy is not actually done, in fact the object is never created on the stack in XX::create() at all, it is simply created directly on the heap. In the first instance, the $1 special variable in the typemap is expanded into result. In the second instance, $1is expanded into XX::create() and this is essentially what the "optimal" attribute is telling SWIG to do.

The "optimal" attribute optimisation is not turned on by default as it has a number of restrictions. Firstly, some code cannot be condensed into a simple call for passing into the copy constructor. One common occurrence is when %exception is used. Consider adding the following %exception to the example:

主要區別是 result 臨時變量再也不保存從 XX::create() 返回的值,而是直接從 XX::create() 返回的值進行復制構造函數調用。使用實現 RVO 的現代編譯器,實際上並不會完成複製,實際上,該對象根本不會在 XX::create() 中的堆棧上建立,而只是在堆上直接建立。首先,將類型映射中的 $1 特殊變量擴展爲 result。在第二種狀況下,將 $1 擴展爲 XX::create(),這實際上就是 optimal 屬性告訴 SWIG 要作的事情。

默認狀況下,optimal 屬性優化未啓用,由於它有許多限制。首先,某些代碼不能被精簡爲傳遞給複製構造函數的簡單調用。一種常見的狀況是使用 %exception。考慮在示例中添加如下 %exception

%exception XX::create() %{
try {
  $action
} catch(const std::exception &e) {
  cout << e.what() << endl;
}
%}

SWIG can detect when the "optimal" attribute cannot be used and will ignore it and in this case will issue the following warning:

SWIG 能夠檢測到什麼時候沒法使用 optimal 屬性,並將其忽略,在這種狀況下,將發出如下警告:

example.i:28: Warning 474: Method XX::create() usage of the optimal attribute ignored
example.i:14: Warning 474: in the out typemap as the following cannot be used to generate
optimal code:
try {
  result = XX::create();
} catch(const std::exception &e) {
  cout << e.what() << endl;
}

It should be clear that the above code cannot be used as the argument to the copy constructor call, that is, for the $1 substitution.

Secondly, if the typemaps uses $1 more than once, then multiple calls to the wrapped function will be made. Obviously that is not very optimal. In fact SWIG attempts to detect this and will issue a warning something like:

應該清楚的是,上面的代碼不能用做複製構造函數調用的參數,即不能用於 $1 替換。

其次,若是類型映射屢次使用 $1,則將屢次調用包裝器函數。顯然,這不是很理想。實際上,SWIG 會嘗試檢測到這一點,並將發出相似如下的警告:

example.i:21: Warning 475: Multiple calls to XX::create() might be generated due to
example.i:7: Warning 475: optimal attribute usage in the out typemap.

However, it doesn't always get it right, for example when $1 is within some commented out code.

可是,它並不老是正確,例如,當 $1 在某些註釋掉的代碼中時。

11.9 多參數類型映射

So far, the typemaps presented have focused on the problem of dealing with single values. For example, converting a single input object to a single argument in a function call. However, certain conversion problems are difficult to handle in this manner. As an example, consider the example at the very beginning of this chapter:

到目前爲止,所提供的類型映射已集中在處理單個值的問題上。例如,在函數調用中將單個輸入對象轉換爲單參數。可是,某些轉換問題很難以這種方式處理。例如,請考慮本章開頭的示例:

int foo(int argc, char *argv[]);

Suppose that you wanted to wrap this function so that it accepted a single list of strings like this:

假設你想包裝此函數,以使其接受單個字符串列表,以下所示:

>>> foo(["ale", "lager", "stout"])

To do this, you not only need to map a list of strings to char *argv[], but the value of int argc is implicitly determined by the length of the list. Using only simple typemaps, this type of conversion is possible, but extremely painful. Multi-argument typemaps help in this situation.

A multi-argument typemap is a conversion rule that specifies how to convert a single object in the target language to a set of consecutive function arguments in C/C++. For example, the following multi-argument maps perform the conversion described for the above example:

爲此,你不只須要將字符串列表映射到 char *argv[],並且 int argc 的值由列表的長度隱式肯定。僅使用簡單的類型映射,這種類型的轉換是可能的,可是很是痛苦。在這種狀況下,多參數類型映射會有所幫助。

多參數類型映射是一種轉換規則,它指定如何將目標語言中的單個對象轉換爲 C/C++ 中的一組連續函數參數。例如,如下多參數映射執行上述示例中描述的轉換:

%typemap(in) (int argc, char *argv[]) {
  int i;
  if (!PyList_Check($input)) {
    PyErr_SetString(PyExc_ValueError, "Expecting a list");
    SWIG_fail;
  }
  $1 = PyList_Size($input);
  $2 = (char **) malloc(($1+1)*sizeof(char *));
  for (i = 0; i < $1; i++) {
    PyObject *s = PyList_GetItem($input, i);
    if (!PyString_Check(s)) {
      free($2);
      PyErr_SetString(PyExc_ValueError, "List items must be strings");
      SWIG_fail;
    }
    $2[i] = PyString_AsString(s);
  }
  $2[i] = 0;
}

%typemap(freearg) (int argc, char *argv[]) {
  if ($2) free($2);
}

/* Required for C++ method overloading */
%typecheck(SWIG_TYPECHECK_STRING_ARRAY) (int argc, char *argv[]) {
  $1 = PyList_Check($input) ? 1 : 0;
}

A multi-argument map is always specified by surrounding the arguments with parentheses as shown. For example:

如上所示,老是經過用括號將參數括起來來指定多參數映射。例如:

%typemap(in) (int argc, char *argv[]) { ... }

Within the typemap code, the variables $1, $2, and so forth refer to each type in the map. All of the usual substitutions apply--just use the appropriate $1 or $2 prefix on the variable name (e.g., $2_type, $1_ltype, etc.)

Multi-argument typemaps always have precedence over simple typemaps and SWIG always performs longest-match searching. Therefore, you will get the following behavior:

在類型映射代碼中,變量 $1$2 等引用映射中的每種類型。全部一般的替換都適用——只需在變量名稱上使用適當的 $1$2 前綴便可(例如 $2_type$1_ltype 等)

多參數類型映射始終優先於簡單類型映射,而 SWIG 始終執行最長匹配搜索。所以,你將獲得如下行爲:

%typemap(in) int argc                              { ... typemap 1 ... }
%typemap(in) (int argc, char *argv[])              { ... typemap 2 ... }
%typemap(in) (int argc, char *argv[], char *env[]) { ... typemap 3 ... }

int foo(int argc, char *argv[]);                   // Uses typemap 2
int bar(int argc, int x);                          // Uses typemap 1
int spam(int argc, char *argv[], char *env[]);     // Uses typemap 3

It should be stressed that multi-argument typemaps can appear anywhere in a function declaration and can appear more than once. For example, you could write this:

應該強調的是,多參數類型映射能夠出如今函數聲明中的任何位置,而且能夠出現屢次。例如,你能夠這樣編寫:

%typemap(in) (int scount, char *swords[]) { ... }
%typemap(in) (int wcount, char *words[]) { ... }

void search_words(int scount, char *swords[], int wcount, char *words[], int maxcount);

Other directives such as %apply and %clear also work with multi-argument maps. For example:

其餘指令,例如 %apply%clear 也能夠與多參數映射一塊兒使用。例如:

%apply (int argc, char *argv[]) {
    (int scount, char *swords[]),
    (int wcount, char *words[])
};
...
%clear (int scount, char *swords[]), (int wcount, char *words[]);
...

Don't forget to also provide a suitable typemap for overloaded functions, such as %typecheck shown for foo above. This is only required if the function is overloaded in C++.

Although multi-argument typemaps may seem like an exotic, little used feature, there are several situations where they make sense. First, suppose you wanted to wrap functions similar to the low-level read() and write() system calls. For example:

不要忘記提供合適的重載函數的類型映射,例如上面爲 foo 顯示的 %typecheck。僅當函數在 C++ 中重載時才須要。

儘管多參數類型映射可能看起來像是一種奇特的、不多使用的功能,但在某些狀況下它們是有意義的。首先,假設你想包裝相似於低級 read()write() 系統調用的函數。例如:

typedef unsigned int size_t;

int read(int fd, void *rbuffer, size_t len);
int write(int fd, void *wbuffer, size_t len);

As is, the only way to use the functions would be to allocate memory and pass some kind of pointer as the second argument---a process that might require the use of a helper function. However, using multi-argument maps, the functions can be transformed into something more natural. For example, you might write typemaps like this:

如此這樣,使用這些函數的惟一方法是分配內存並傳遞某種指針做爲第二個參數,該過程可能須要使用輔助函數。可是,使用多參數映射能夠將功能轉換爲更天然的功能。例如,你能夠這樣編寫類型映射:

// typemap for an outgoing buffer
%typemap(in) (void *wbuffer, size_t len) {
  if (!PyString_Check($input)) {
    PyErr_SetString(PyExc_ValueError, "Expecting a string");
    SWIG_fail;
  }
  $1 = (void *) PyString_AsString($input);
  $2 = PyString_Size($input);
}

// typemap for an incoming buffer
%typemap(in) (void *rbuffer, size_t len) {
  if (!PyInt_Check($input)) {
    PyErr_SetString(PyExc_ValueError, "Expecting an integer");
    SWIG_fail;
  }
  $2 = PyInt_AsLong($input);
  if ($2 < 0) {
    PyErr_SetString(PyExc_ValueError, "Positive integer expected");
    SWIG_fail;
  }
  $1 = (void *) malloc($2);
}

// Return the buffer.  Discarding any previous return result
%typemap(argout) (void *rbuffer, size_t len) {
  Py_XDECREF($result);   /* Blow away any previous result */
  if (result < 0) {      /* Check for I/O error */
    free($1);
    PyErr_SetFromErrno(PyExc_IOError);
    return NULL;
  }
  $result = PyString_FromStringAndSize($1, result);
  free($1);
}

(note: In the above example, $result and result are two different variables. result is the real C datatype that was returned by the function. $result is the scripting language object being returned to the interpreter.).

Now, in a script, you can write code that simply passes buffers as strings like this:

(注意:在上面的示例中,$resultresult 是兩個不一樣的變量。result 是函數返回的實際 C 數據類型。$result 是要返回到解釋器的腳本語言對象 )。

如今,在腳本中,你能夠編寫簡單地將緩衝區做爲字符串傳遞的代碼,以下所示:

>>> f = example.open("Makefile")
>>> example.read(f, 40)
'TOP        = ../..\nSWIG       = $(TOP)/.'
>>> example.read(f, 40)
'./swig\nSRCS       = example.c\nTARGET    '
>>> example.close(f)
0
>>> g = example.open("foo", example.O_WRONLY | example.O_CREAT, 0644)
>>> example.write(g, "Hello world\n")
12
>>> example.write(g, "This is a test\n")
15
>>> example.close(g)
0
>>>

A number of multi-argument typemap problems also arise in libraries that perform matrix-calculations--especially if they are mapped onto low-level Fortran or C code. For example, you might have a function like this:

在執行矩陣計算的庫中,還會出現許多多參數類型映射問題,尤爲是若是將它們映射到低級 Fortran 或 C 代碼上。例如,你可能具備如下函數:

int is_symmetric(double *mat, int rows, int columns);

In this case, you might want to pass some kind of higher-level object as an matrix. To do this, you could write a multi-argument typemap like this:

在這種狀況下,你可能須要傳遞某種高級對象做爲矩陣。爲此,你能夠編寫一個以下所示的多參數類型映射:

%typemap(in) (double *mat, int rows, int columns) {
  MatrixObject *a;
  a = GetMatrixFromObject($input);     /* Get matrix somehow */

  /* Get matrix properties */
  $1 = GetPointer(a);
  $2 = GetRows(a);
  $3 = GetColumns(a);
}

This kind of technique can be used to hook into scripting-language matrix packages such as Numeric Python. However, it should also be stressed that some care is in order. For example, when crossing languages you may need to worry about issues such as row-major vs. column-major ordering (and perform conversions if needed). Note that multi-argument typemaps cannot deal with non-consecutive C/C++ arguments; a workaround such as a helper function re-ordering the arguments to make them consecutive will need to be written.

這種技術可用於鏈接腳本語言矩陣包,例如 Numeric Python。可是,還應該強調,必定要謹慎。例如,在使用多種語言時,你可能須要擔憂行優先與列優先的排序(並在須要時執行轉換)。注意,多參數類型映射不能處理非連續的 C/C++ 參數。須要編寫一種變通方法,例如輔助函數,將參數從新排序以使其連續。

11.10 類型映射警告

Warnings can be added to typemaps so that SWIG generates a warning message whenever the typemap is used. See the information in the issuing warnings section.

能夠將警告添加到類型映射,以便每當使用類型映射時 SWIG 都會生成警告消息。請參閱發佈警告章節中的信息。

11.11 類型映射片斷

The primary purpose of fragments is to reduce code bloat that repeated use of typemap code can lead to. Fragments are snippets of code that can be thought of as code dependencies of a typemap. If a fragment is used by more than one typemap, then the snippet of code within the fragment is only generated once. Code bloat is typically reduced by moving typemap code into a support function and then placing the support function into a fragment.

For example, if you have a very long typemap

片斷的主要目的是減小重複使用類型映射代碼可能致使的代碼膨脹。片斷是代碼片斷,能夠將其視爲類型映射的代碼依賴項。若是一個片斷被多個類型映射使用,則該片斷內的代碼片斷僅生成一次。一般能夠經過將類型映射代碼移入支持函數,而後將支持函數放入片斷中來減小代碼膨脹。

例如,若是你的類型映射很長

%typemap(in) MyClass * {
  MyClass *value = 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:

相同的編組代碼一般在多個類型映射中重複,例如 invarindirectorout 等。SWIG 爲須要該類型映射代碼的每一個參數複製代碼,從而很容易致使所生成代碼中的代碼膨脹。爲了消除這種狀況,請定義一個包含通用編組代碼的片斷:

%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:

當須要 MyClassinvarin 類型映射時,將名爲 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:

  1. A fragment is added to the wrapping code only once. When using the MyClass * typemaps above and wrapping the method:

而且它們遵循預處理分隔符章節中提到的常規預處理規則。如下是使用片斷的一些規則和準則:

  1. 一個片斷僅被添加到包裝代碼一次。當使用上面的 MyClass * 類型映射幷包裝方法時:
void foo(MyClass *a, MyClass *b);

the generated code will look something like:

將會產生相似下面的代碼:

MyClass *AsMyClass(PyObject *obj) {
...
}

void _wrap_foo(...) {
....
arg1 = AsMyClass(obj1);
arg2 = AsMyClass(obj2);
...
foo(arg1, arg2);
}

even as there is duplicated typemap code to process both a and b, the AsMyClass method will be defined only once.

  1. 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

即便存在重複的類型映射代碼來處理 abAsMyClass 方法也只會定義一次。

  1. 一個片斷只能定義一次。若是有多個定義,則第一個定義是使用的定義。全部其餘定義都被忽略。例如,若是你有
%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.

  1. Fragment names cannot contain commas.

  2. A fragment can use one or more additional fragments, for example:

僅使用第一個定義。這樣,你能夠經過在 %include 庫以前定義片斷來覆蓋 SWIG 庫中的默認片斷。請注意,此行爲與類型映射相反,後者以最後定義或應用的類型映射爲準。片斷遵循先進先出的約定,由於它們是全局的,而類型映射則是局部的。

  1. 片斷名稱不能包含逗號。
  2. 一個片斷可使用一個或多個其餘片斷,例如:
%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.

  1. A fragment can have dependencies on a number of other fragments, for example:

在這種狀況下,發出 AsMyClass 片斷時,也會觸發包含 <limits.h> 片斷。

  1. 一個片斷能夠依賴於許多其餘片斷,例如:
%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.

  1. 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 時,三個從屬片斷 frag1frag2frag3 也被拉入。請注意,因爲 bigframent 爲空(空字符串——""),所以不添加任何代碼自己,但僅觸發其餘片斷的包含。

  1. 一個類型映射也可使用多個片斷,可是因爲語法不一樣,你須要在逗號分隔的列表中指定從屬片斷。考慮:
%typemap(in, fragment="frag1, frag2, frag3") {...}

which is equivalent to:

等效於:

%typemap(in, fragment="bigfragment") {...}

when used with the "bigfragment" defined above.

  1. Finally, you can force the inclusion of a fragment at any point in the generated code as follows:

與上面定義的 bigfragment 一塊兒使用時。

  1. 最後,你能夠按如下步驟在生成的代碼中的任何位置強制包含片斷:
%fragment("bigfragment");

which is very useful inside a template class, for example.

Most readers will probably want to skip the next two sub-sections on advanced fragment usage unless a desire to really get to grips with some powerful but tricky macro and fragment usage that is used in parts of the SWIG typemap library.

例如,這在模板類內部很是有用。

除非但願真正掌握 SWIG 類型映射庫的某些部分中使用的某些功能強大但棘手的宏和片斷用法,不然大多數讀者可能會但願跳過接下來的兩節有關高級片斷用法的小節。

11.11.1 片斷類型特化

Fragments can be type specialized. The syntax is as follows:

片斷能夠是類型特化。語法以下:

%fragment("name", "header") { ...a type independent fragment... }
%fragment("name"{type}, "header") { ...a type dependent fragment...  }

where type is a C/C++ type. Like typemaps, fragments can also be used inside templates, for example:

其中,type 是 C/C++ 類型。像類型映射同樣,片斷也能夠在模板內部使用,例如:

template <class T>
struct A {
  %fragment("incode"{A<T>}, "header") {
    ... 'incode' specialized fragment ...
  }

  %typemap(in, fragment="incode"{A<T>}) {
    ... here we use the 'type specialized' fragment "incode"{A<T>} ...
  }
};

11.11.2 片斷與自動類型映射特化

Since fragments can be type specialized, they can be elegantly used to specialize typemaps. For example, if you have something like:

因爲片斷能夠是類型特化的,所以能夠很好地用於特化類型映射。例如,若是你有如下內容:

%fragment("incode"{float}, "header") {
  float in_method_float(PyObject *obj) {
    ...
  }
}

%fragment("incode"{long}, "header") {
  float in_method_long(PyObject *obj) {
    ...
  }
}

// %my_typemaps macro definition
%define %my_typemaps(Type)
%typemap(in, fragment="incode"{Type}) Type {
  value = in_method_##Type(obj);
}
%enddef

%my_typemaps(float);
%my_typemaps(long);

then the proper "incode"{float} or "incode"{long} fragment will be used, and the in_method_floatand in_method_long methods will be called whenever the float or long types are used as input parameters.

This feature is used a lot in the typemaps shipped in the SWIG library for some scripting languages. The interested (or very brave) reader can take a look at the fragments.swg file shipped with SWIG to see this in action.

那麼將使用正確的 "incode"{float}"incode"{long} 片斷,而且每當使用 floatlong 類型時,就會調用 in_method_floatin_method_long 方法 做爲輸入參數。

SWIG 庫附帶的類型映射中的某些腳本語言常用此功能。有興趣的(或很是勇敢的)讀者能夠查看 SWIG 附帶的 fragments.swg 文件,以瞭解實際狀況。

11.12 運行時類型檢查器

Most scripting languages need type information at run-time. This type information can include how to construct types, how to garbage collect types, and the inheritance relationships between types. If the language interface does not provide its own type information storage, the generated SWIG code needs to provide it.

Requirements for the type system:

  • Store inheritance and type equivalence information and be able to correctly re-create the type pointer.
  • Share type information between modules.
  • Modules can be loaded in any order, regardless of actual type dependency.
  • Avoid the use of dynamically allocated memory, and library/system calls in general.
  • Provide a reasonably fast implementation, minimizing the lookup time for all language modules.
  • Custom, language specific information can be attached to types.
  • Modules can be unloaded from the type system.

大多數腳本語言在運行時都須要類型信息。此類型信息能夠包括如何構造類型,如何垃圾回收類型,以及類型之間的繼承關係。若是語言接口不提供本身的類型信息存儲,則生成的 SWIG 代碼須要提供它。

類型系統要求:

  • 存儲繼承和類型等效信息,並可以正確地從新建立類型指針。
  • 在模塊之間共享類型信息。
  • 模塊能夠以任何順序加載,而無論實際的類型依賴性如何。
  • 避免通常使用動態分配的內存和庫/系統調用。
  • 提供合理快速的實施,以最小化全部語言模塊的查找時間。
  • 自定義,特定於語言的信息能夠附加到類型上。
  • 能夠從類型系統中卸載模塊。

11.12.1 實現

The run-time type checker is used by many, but not all, of SWIG's supported target languages. The run-time type checker features are not required and are thus not used for statically typed languages such as Java and C#. The scripting and scheme based languages rely on it and it forms a critical part of SWIG's operation for these languages.

When pointers, arrays, and objects are wrapped by SWIG, they are normally converted into typed pointer objects. For example, an instance of Foo * might be a string encoded like this:

SWIG 支持的許多(但不是所有)目標語言都使用運行時類型檢查器。運行時類型檢查器功能不是必需的,所以不用於 Java 和 C# 之類的靜態類型語言。基於腳本和框架的語言都依賴它,而且它構成了 SWIG 對這些語言的操做的關鍵部分。

當指針,數組和對象由 SWIG 包裝時,它們一般會轉換爲類型化的指針對象。例如,Foo * 的實例多是這樣編碼的字符串:

_108e688_p_Foo

At a basic level, the type checker simply restores some type-safety to extension modules. However, the type checker is also responsible for making sure that wrapped C++ classes are handled correctly---especially when inheritance is used. This is especially important when an extension module makes use of multiple inheritance. For example:

從根本上講,類型檢查器只是將一些類型安全性恢復到擴展模塊。可是,類型檢查器還負責確保正確處理包裝的 C++ 類——尤爲是在使用繼承時。當擴展模塊利用多重繼承時,這一點尤爲重要。例如:

class Foo {
public:
  int x;
};

class Bar {
public:
  int y;
};

class FooBar : public Foo, public Bar {
public:
  int z;
};

When the class FooBar is organized in memory, it contains the contents of the classes Foo and Bar as well as its own data members. For example:

當在內存中組織類 FooBar 時,它包含類 FooBar 的內容以及它本身的數據成員。例如:

FooBar --> | -----------|  <-- Foo
           | int x |
           | ----- |<-- Bar
           | int y        |
           | ------------ |
           | int z        |
           | ------------ |

Because of the way that base class data is stacked together, the casting of a Foobar * to either of the base classes may change the actual value of the pointer. This means that it is generally not safe to represent pointers using a simple integer or a bare void *---type tags are needed to implement correct handling of pointer values (and to make adjustments when needed).

In the wrapper code generated for each language, pointers are handled through the use of special type descriptors and conversion functions. For example, if you look at the wrapper code for Python, you will see code similar to the following (simplified for brevity):

因爲將基類數據堆疊在一塊兒的方式,將 Foobar * 強制轉換爲任一基類均可能會更改指針的實際值。這意味着使用一個簡單的整數或一個簡單的 void *——來表示指針一般是不安全的——須要使用類型標記來實現對指針值的正確處理(並在須要時進行調整)。

在爲每種語言生成的包裝器代碼中,經過使用特殊的類型描述符和轉換函數來處理指針。例如,若是查看 Python 的包裝器代碼,你將看到相似於如下代碼(爲簡潔起見簡化):

if (!SWIG_IsOK(SWIG_ConvertPtr(obj0, (void **) &arg1, SWIGTYPE_p_Foo, 0))) {
  SWIG_exception_fail(SWIG_TypeError, "in method 'GrabVal', expecting type Foo");
}

In this code, SWIGTYPE_p_Foo is the type descriptor that describes Foo *. The type descriptor is actually a pointer to a structure that contains information about the type name to use in the target language, a list of equivalent typenames (via typedef or inheritance), and pointer value handling information (if applicable). The SWIG_ConvertPtr() function is simply a utility function that takes a pointer object in the target language and a type-descriptor object and uses this information to generate a C++ pointer. The SWIG_IsOKmacro checks the return value for errors and SWIG_exception_fail can be called to raise an exception in the target language. However, the exact name and calling conventions of the conversion function depends on the target language (see language specific chapters for details).

The actual type code is in swigrun.swg, and gets inserted near the top of the generated swig wrapper file. The phrase "a type X that can cast into a type Y" means that given a type X, it can be converted into a type Y. In other words, X is a derived class of Y or X is a typedef of Y. The structure to store type information looks like this:

在這段代碼中,SWIGTYPE_p_Foo 是描述 Foo * 的類型描述符。類型描述符其實是指向結構體的指針,該結構體包含有關要在目標語言中使用的類型名稱的信息,等效類型名稱的列表(經過 typedef 或繼承)以及指針值處理信息(若是適用)。SWIG_ConvertPtr() 函數只是一個實用函數,它接受目標語言中的指針對象和類型描述符對象,並使用此信息生成 C++ 指針。SWIG_IsOK 宏檢查錯誤的返回值,而且能夠調用 SWIG_exception_fail 引起目標語言中的異常。可是,轉換函數的確切名稱和調用約定取決於目標語言(有關詳細信息,請參見特定於語言的章節)。

實際的類型代碼在 swigrun.swg 中,並插入到生成的 swig 包裝文件頂部附近。短語「能夠轉換爲 Y 類型的 X 類型」表示給定 X 類型,能夠將其轉換爲 Y 類型。換句話說,XY 的派生類,或者 XYtypedef。存儲類型信息的結構體以下所示:

/* Structure to store information on one type */
typedef struct swig_type_info {
  const char *name;             /* mangled name of this type */
  const char *str;              /* human readable name for this type */
  swig_dycast_func dcast;       /* dynamic cast function down a hierarchy */
  struct swig_cast_info *cast;  /* Linked list of types that can cast into this type */
  void *clientdata;             /* Language specific type data */
} swig_type_info;

/* Structure to store a type and conversion function used for casting */
typedef struct swig_cast_info {
  swig_type_info *type;          /* pointer to type that is equivalent to this type */
  swig_converter_func converter; /* function to cast the void pointers */
  struct swig_cast_info *next;   /* pointer to next cast in linked list */
  struct swig_cast_info *prev;   /* pointer to the previous cast */
} swig_cast_info;

Each swig_type_info stores a linked list of types that it is equivalent to. Each entry in this doubly linked list stores a pointer back to another swig_type_info structure, along with a pointer to a conversion function. This conversion function is used to solve the above problem of the FooBar class, correctly returning a pointer to the type we want.

The basic problem we need to solve is verifying and building arguments passed to functions. So going back to the SWIG_ConvertPtr() function example from above, we are expecting a Foo * and need to check if obj0 is in fact a Foo *. From before, SWIGTYPE_p_Foo is just a pointer to the swig_type_infostructure describing Foo *. So we loop through the linked list of swig_cast_info structures attached to SWIGTYPE_p_Foo. If we see that the type of obj0 is in the linked list, we pass the object through the associated conversion function and then return a positive. If we reach the end of the linked list without a match, then obj0 can not be converted to a Foo * and an error is generated.

Another issue needing to be addressed is sharing type information between multiple modules. More explicitly, we need to have ONE swig_type_info for each type. If two modules both use the type, the second module loaded must lookup and use the swig_type_info structure from the module already loaded. Because no dynamic memory is used and the circular dependencies of the casting information, loading the type information is somewhat tricky, and not explained here. A complete description is in the Lib/swiginit.swg file (and near the top of any generated file).

Each module has one swig_module_info structure which looks like this:

每一個 swig_type_info 都存儲一個等效的類型的鏈表。這個雙向連接列表中的每一個條目都存儲着一個指向另外一個 swig_type_info 結構體的指針,以及一個指向轉換函數的指針。此轉換函數用於解決 FooBar 類的上述問題,正確返回指向所需類型的指針。

咱們須要解決的基本問題是驗證和構建傳遞給函數的參數。所以,從上面回到 SWIG_ConvertPtr() 函數示例,咱們指望的是 Foo *,而且須要檢查 obj0 其實是否爲 Foo *。從前,SWIGTYPE_p_Foo 只是指向描述 Foo *swig_type_info 結構體的指針。所以,咱們遍歷附加到 SWIGTYPE_p_Fooswig_cast_info 結構體的連接列表。若是咱們看到 obj0 的類型在鏈表中,則將對象傳遞給關聯的轉換函數,而後返回一個正數。若是咱們到達鏈表的末尾但沒有匹配項,則沒法將 obj0 轉換爲 Foo * 並生成錯誤。

須要解決的另外一個問題是在多個模塊之間共享類型信息。更明確地說,咱們須要爲每種類型使用一個 swig_type_info。若是兩個模塊都使用該類型,則加載的第二個模塊必須從已加載的模塊中查找並使用 swig_type_info 結構體。由於沒有使用動態內存,並且轉換信息的循環依賴關係,因此加載類型信息有些棘手,這裏再也不贅述。完整的描述在 Lib/swiginit.swg 文件中(而且在任何生成的文件的頂部附近)。

每一個模塊都有一個 swig_module_info 結構體,以下所示:

/* Structure used to store module information
 * Each module generates one structure like this, and the runtime collects
 * all of these structures and stores them in a circularly linked list.*/
typedef struct swig_module_info {
  swig_type_info **types;         /* Array of pointers to swig_type_info structs in this module */
  int size;                       /* Number of types in this module */
  struct swig_module_info *next;  /* Pointer to next element in circularly linked list */
  swig_type_info **type_initial;  /* Array of initially generated type structures */
  swig_cast_info **cast_initial;  /* Array of initially generated casting structures */
  void *clientdata;               /* Language specific module data */
} swig_module_info;

Each module stores an array of pointers to swig_type_info structures and the number of types in this module. So when a second module is loaded, it finds the swig_module_info structure for the first module and searches the array of types. If any of its own types are in the first module and have already been loaded, it uses those swig_type_info structures rather than creating new ones. These swig_module_info structures are chained together in a circularly linked list.

每一個模塊存儲一個指向 swig_type_info 結構體的指針數組以及該模塊中類型的數量。所以,在加載第二個模塊時,它將爲第一個模塊找到 swig_module_info 結構體,並搜索類型數組。若是在第一個模塊中有任何本身的類型而且已經被加載,則它使用那些 swig_type_info 結構體而不是建立新的結構體。這些 swig_module_info 結構體以循環連接列表的形式連接在一塊兒。

11.12.2 使用

This section covers how to use these functions from typemaps. To learn how to call these functions from external files (not the generated _wrap.c file), see the External access to the run-time system section.

When pointers are converted in a typemap, the typemap code often looks similar to this:

本節介紹如何使用類型映射中的這些功能。要了解如何從外部文件(而不是生成的 _wrap.c 文件)中調用這些函數,請參見對運行時系統的外部訪問章節。

在類型映射中轉換指針時,類型映射代碼一般看起來相似於如下內容:

%typemap(in) Foo * {
  if (!SWIG_IsOK(SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor, 0))) {
    SWIG_exception_fail(SWIG_TypeError, "in method '$symname', expecting type Foo");
  }
}

The most critical part is the typemap is the use of the $1_descriptor special variable. When placed in a typemap, this is expanded into the SWIGTYPE_* type descriptor object above. As a general rule, you should always use $1_descriptor instead of trying to hard-code the type descriptor name directly.

There is another reason why you should always use the $1_descriptor variable. When this special variable is expanded, SWIG marks the corresponding type as "in use." When type-tables and type information is emitted in the wrapper file, descriptor information is only generated for those datatypes that were actually used in the interface. This greatly reduces the size of the type tables and improves efficiency.

Occasionally, you might need to write a typemap that needs to convert pointers of other types. To handle this, the special variable macro $descriptor(type) covered earlier can be used to generate the SWIG type descriptor name for any C datatype. For example:

最關鍵的部分是類型映射是 $1_descriptor 特殊變量的使用。當放置在類型映射中時,它會擴展到上面的 SWIGTYPE_* 類型描述符對象中。一般,應該始終使用 $1_descriptor 而不是嘗試直接對類型描述符名稱進行硬編碼。

還有另外一個緣由,爲何你應該始終使用 $1_descriptor 變量。擴展此特殊變量後,SWIG 會將相應的類型標記爲「使用中」。當在包裝文件中發出類型表和類型信息時,僅爲接口中實際使用的那些數據類型生成描述符信息。這大大減少了類型表的大小並提升了效率。

有時,你可能須要編寫一個類型映射,該類型映射須要轉換其餘類型的指針。爲了解決這個問題,前面介紹的特殊變量宏 $descriptor(type) 可用於爲任何 C 數據類型生成 SWIG 類型描述符名稱。例如:

%typemap(in) Foo * {
  if (!SWIG_IsOK(SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor, 0))) {
    Bar *temp;
    if (!SWIG_IsOK(SWIG_ConvertPtr($input, (void **) &temp, $descriptor(Bar *), 0))) {
      SWIG_exception_fail(SWIG_TypeError, "in method '$symname', expecting type Foo or Bar");
    }
    $1 = (Foo *)temp;
  }
}

The primary use of $descriptor(type) is when writing typemaps for container objects and other complex data structures. There are some restrictions on the argument---namely it must be a fully defined C datatype. It can not be any of the special typemap variables.

In certain cases, SWIG may not generate type-descriptors like you expect. For example, if you are converting pointers in some non-standard way or working with an unusual combination of interface files and modules, you may find that SWIG omits information for a specific type descriptor. To fix this, you may need to use the %types directive. For example:

$descriptor(type) 的主要用途是爲容器對象和其餘複雜數據結構編寫類型映射時。參數有一些限制——即它必須是徹底定義的 C 數據類型。它不能是任何特殊的類型映射變量。

在某些狀況下,SWIG 可能不會生成你指望的類型描述符。例如,若是你以某種非標準的方式轉換指針或使用接口文件和模塊的異常組合,則可能會發現 SWIG 忽略了特定類型描述符的信息。爲了解決這個問題,你可能須要使用 %types 指令。例如:

%types(int *, short *, long *, float *, double *);

When %types is used, SWIG generates type-descriptor information even if those datatypes never appear elsewhere in the interface file.

Further details about the run-time type checking can be found in the documentation for individual language modules. Reading the source code may also help. The file Lib/swigrun.swg in the SWIG library contains all of the source of the generated code for type-checking. This code is also included in every generated wrapped file so you probably just look at the output of SWIG to get a better sense for how types are managed.

當使用 %types時,SWIG 會生成類型描述符信息,即便這些數據類型從不出如今接口文件的其餘位置。

有關運行時類型檢查的更多詳細信息,請參見各個語言模塊的文檔。閱讀源代碼也可能會有所幫助。SWIG 庫中的 Lib/swigrun.swg 文件包含用於類型檢查的生成代碼的全部源。該代碼也包含在每一個生成的包裝文件中,所以你可能只需查看 SWIG 的輸出便可更好地瞭解如何管理類型。

11.13 類型映射與重載

This section does not apply to the statically typed languages like Java and C#, where overloading of the types is handled much like C++ by generating overloaded methods in the target language. In many of the other target languages, SWIG still fully supports C++ overloaded methods and functions. For example, if you have a collection of functions like this:

本章節不適用於 Java 和 C# 等靜態類型的語言,在這些類型中,類型的重載與 C++ 同樣,是經過在目標語言中生成重載的方法來處理的。在許多其餘目標語言中,SWIG 仍徹底支持 C++ 重載方法和函數。例如,若是你具備如下功能集合:

int foo(int x);
int foo(double x);
int foo(char *s, int y);

You can access the functions in a normal way from the scripting interpreter:

你能夠從腳本解釋器以常規方式訪問函數:

# Python
foo(3)           # foo(int)
foo(3.5)         # foo(double)
foo("hello", 5)  # foo(char *, int)

# Tcl
foo 3            # foo(int)
foo 3.5          # foo(double)
foo hello 5      # foo(char *, int)

To implement overloading, SWIG generates a separate wrapper function for each overloaded method. For example, the above functions would produce something roughly like this:

爲了實現重載,SWIG 爲每一個重載方法生成一個單獨的包裝器函數。例如,以上函數將產生大體以下所示的內容:

// wrapper pseudocode
_wrap_foo_0(argc, args[]) {       // foo(int)
  int arg1;
  int result;
  ...
  arg1 = FromInteger(args[0]);
  result = foo(arg1);
  return ToInteger(result);
}

_wrap_foo_1(argc, args[]) {       // foo(double)
  double arg1;
  int result;
  ...
  arg1 = FromDouble(args[0]);
  result = foo(arg1);
  return ToInteger(result);
}

_wrap_foo_2(argc, args[]) {       // foo(char *, int)
  char *arg1;
  int   arg2;
  int result;
  ...
  arg1 = FromString(args[0]);
  arg2 = FromInteger(args[1]);
  result = foo(arg1, arg2);
  return ToInteger(result);
}

Next, a dynamic dispatch function is generated:

接着生成動態調度函數:

_wrap_foo(argc, args[]) {
  if (argc == 1) {
    if (IsInteger(args[0])) {
      return _wrap_foo_0(argc, args);
    }
    if (IsDouble(args[0])) {
      return _wrap_foo_1(argc, args);
    }
  }
  if (argc == 2) {
    if (IsString(args[0]) && IsInteger(args[1])) {
      return _wrap_foo_2(argc, args);
    }
  }
  error("No matching function!\n");
}

The purpose of the dynamic dispatch function is to select the appropriate C++ function based on argument types---a task that must be performed at runtime in most of SWIG's target languages.

The generation of the dynamic dispatch function is a relatively tricky affair. Not only must input typemaps be taken into account (these typemaps can radically change the types of arguments accepted), but overloaded methods must also be sorted and checked in a very specific order to resolve potential ambiguity. A high-level overview of this ranking process is found in the "SWIG and C++" chapter. What isn't mentioned in that chapter is the mechanism by which it is implemented---as a collection of typemaps.

To support dynamic dispatch, SWIG first defines a general purpose type hierarchy as follows:

動態調度函數的目的是根據參數類型選擇適當的 C++ 函數,這是大多數 SWIG 目標語言都必須在運行時執行的任務。

動態調度函數的生成是一個比較棘手的事情。不只必須考慮輸入類型映射(這些類型映射能夠從根本上改變接受的參數的類型),並且還必須以很是特定的順序對重載方法進行排序和檢查,以解決潛在的歧義。《SWIG 和 C++》一章中提供了有關此排名過程的高級概述。在這一章中沒有提到的是實現它的機制——做爲類型映射的集合。

爲了支持動態調度,SWIG 首先定義通用類型層次結構,以下所示:

Symbolic Name                   Precedence Value
------------------------------  ------------------
SWIG_TYPECHECK_POINTER           0
SWIG_TYPECHECK_VOIDPTR           10
SWIG_TYPECHECK_BOOL              15
SWIG_TYPECHECK_UINT8             20
SWIG_TYPECHECK_INT8              25
SWIG_TYPECHECK_UINT16            30
SWIG_TYPECHECK_INT16             35
SWIG_TYPECHECK_UINT32            40
SWIG_TYPECHECK_INT32             45
SWIG_TYPECHECK_UINT64            50
SWIG_TYPECHECK_INT64             55
SWIG_TYPECHECK_UINT128           60
SWIG_TYPECHECK_INT128            65
SWIG_TYPECHECK_INTEGER           70
SWIG_TYPECHECK_FLOAT             80
SWIG_TYPECHECK_DOUBLE            90
SWIG_TYPECHECK_COMPLEX           100
SWIG_TYPECHECK_UNICHAR           110
SWIG_TYPECHECK_UNISTRING         120
SWIG_TYPECHECK_CHAR              130
SWIG_TYPECHECK_STRING            140
SWIG_TYPECHECK_BOOL_ARRAY        1015
SWIG_TYPECHECK_INT8_ARRAY        1025
SWIG_TYPECHECK_INT16_ARRAY       1035
SWIG_TYPECHECK_INT32_ARRAY       1045
SWIG_TYPECHECK_INT64_ARRAY       1055
SWIG_TYPECHECK_INT128_ARRAY      1065
SWIG_TYPECHECK_FLOAT_ARRAY       1080
SWIG_TYPECHECK_DOUBLE_ARRAY      1090
SWIG_TYPECHECK_CHAR_ARRAY        1130
SWIG_TYPECHECK_STRING_ARRAY      1140

(These precedence levels are defined in swig.swg, a library file that's included by all target language modules.)

In this table, the precedence-level determines the order in which types are going to be checked. Low values are always checked before higher values. For example, integers are checked before floats, single values are checked before arrays, and so forth.

Using the above table as a guide, each target language defines a collection of "typecheck" typemaps. The following excerpt from the Python module illustrates this:

(這些優先級在 swig.swg 中定義,swig.swg 是全部目標語言模塊都包含的庫文件。)

在此表中,優先級肯定要檢查的類型的順序。始終先檢查低值,而後再檢查高值。例如,在浮點數以前檢查整數,在數組以前檢查單個值,依此類推。

使用上表做爲指導,每種目標語言都定義了 typecheck 類型映射的集合。如下 Python 模塊摘錄說明了這一點:

/* Python type checking rules */
/* Note:  %typecheck(X) is a macro for %typemap(typecheck, precedence=X) */

%typecheck(SWIG_TYPECHECK_INTEGER)
  int, short, long,
  unsigned int, unsigned short, unsigned long,
  signed char, unsigned char,
  long long, unsigned long long,
  const int &, const short &, const long &,
  const unsigned int &, const unsigned short &, const unsigned long &,
  const long long &, const unsigned long long &,
  enum SWIGTYPE,
  bool, const bool &
{
  $1 = (PyInt_Check($input) || PyLong_Check($input)) ? 1 : 0;
}

%typecheck(SWIG_TYPECHECK_DOUBLE)
  float, double,
  const float &, const double &
{
  $1 = (PyFloat_Check($input) || PyInt_Check($input) || PyLong_Check($input)) ? 1 : 0;
}

%typecheck(SWIG_TYPECHECK_CHAR) char {
  $1 = (PyString_Check($input) && (PyString_Size($input) == 1)) ? 1 : 0;
}

%typecheck(SWIG_TYPECHECK_STRING) char * {
  $1 = PyString_Check($input) ? 1 : 0;
}

%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER, noblock=1) SWIGTYPE * {
  void *vptr = 0;
  int res = SWIG_ConvertPtr($input, &vptr, $1_descriptor, 0);
  $1 = SWIG_IsOK(res) ? 1 : 0;
}

%typecheck(SWIG_TYPECHECK_POINTER) PyObject * {
  $1 = ($input != 0);
}

It might take a bit of contemplation, but this code has merely organized all of the basic C++ types, provided some simple type-checking code, and assigned each type a precedence value.

Finally, to generate the dynamic dispatch function, SWIG uses the following algorithm:

  • Overloaded methods are first sorted by the number of required arguments.
  • Methods with the same number of arguments are then sorted by precedence values of argument types.
  • Typecheck typemaps are then emitted to produce a dispatch function that checks arguments in the correct order.

If you haven't written any typemaps of your own, it is unnecessary to worry about the typechecking rules. However, if you have written new input typemaps, you might have to supply a typechecking rule as well. An easy way to do this is to simply copy one of the existing typechecking rules. Here is an example,

這可能須要一些考慮,可是此代碼僅組織了全部基本 C++ 類型,提供了一些簡單的類型檢查代碼,併爲每種類型分配了優先級值。

最後,爲了生成動態調度功能,SWIG 使用如下算法:

  • 重載的方法首先按所需參數的數量排序。
  • 而後,將具備相同數量參數的方法按參數類型的優先級值排序。
  • 而後發出 typecheck 類型映射,以產生一個調度函數,該函數以正確的順序檢查參數。

若是你還沒有編寫任何類型映射,則沒必要擔憂類型檢查規則。可是,若是你編寫了新的輸入類型映射,則可能還必須提供類型檢查規則。一種簡單的方法是簡單地複製現有的類型檢查規則之一。這是一個例子

// Typemap for a C++ string
%typemap(in) std::string {
  if (PyString_Check($input)) {
    $1 = std::string(PyString_AsString($input));
  } else {
    SWIG_exception(SWIG_TypeError, "string expected");
  }
}
// Copy the typecheck code for "char *".
%typemap(typecheck) std::string = char *;

The bottom line: If you are writing new typemaps and you are using overloaded methods, you will probably have to write new typecheck code or copy and modify existing typecheck code.

If you write a typecheck typemap and omit the precedence level, for example commenting it out as shown below:

底線:若是你正在編寫新的類型映射,而且使用的是重載方法,則可能必須編寫新的類型檢查代碼或複製和修改現有的類型檢查代碼。

若是編寫類型檢查類型映射並忽略優先級,例如將其註釋掉,以下所示:

%typemap(typecheck /*, precedence=SWIG_TYPECHECK_INTEGER*/) int {
  $1 = PyInt_Check($input) ? 1 : 0;
}

then the type is given a precedence higher than any other known precedence level and a warning is issued:

而後爲該類型賦予比其餘任何已知優先級高的優先級,併發出警告

example.i:18: Warning 467: Overloaded method foo(int) not supported (incomplete type checking rule - no precedence level in typecheck typemap for 'int').

Notes:

  • Typecheck typemaps are not used for non-overloaded methods. Because of this, it is still always necessary to check types in any "in" typemaps.
  • The dynamic dispatch process is only meant to be a heuristic. There are many corner cases where SWIG simply can't disambiguate types to the same degree as C++. The only way to resolve this ambiguity is to use the %rename directive to rename one of the overloaded methods (effectively eliminating overloading).
  • Typechecking may be partial. For example, if working with arrays, the typecheck code might simply check the type of the first array element and use that to dispatch to the correct function. Subsequent "in" typemaps would then perform more extensive type-checking.
  • Make sure you read the section on overloading in the "SWIG and C++" chapter.

注意:

  • typecheck 類型映射不適用於非重載方法。所以,仍然始終須要檢查任何 in 類型映射中的類型。
  • 動態調度過程僅是一種啓發式方法。在許多特殊狀況下,SWIG 不能徹底消除類型與 C++ 相同的歧義。解決此歧義的惟一方法是使用 %rename 指令重命名其中一種重載方法(有效消除重載)。
  • 類型檢查多是部分的。例如,若是使用數組,則類型檢查代碼能夠簡單地檢查第一個數組元素的類型,而後使用它來分派給正確的函數。隨後的 in 類型映射將執行更普遍的類型檢查。
  • 確保你已閱讀《SWIG 和 C++》一章中有關重載的部分。

11.14 %apply%clear 詳情

In order to implement certain kinds of program behavior, it is sometimes necessary to write sets of typemaps. For example, to support output arguments, one often writes a set of typemaps like this:

爲了實現某些類型的程序行爲,有時有必要編寫類型映射集。例如,爲了支持輸出參數,一般會編寫這樣的一組類型映射:

%typemap(in, numinputs=0) int *OUTPUT (int temp) {
  $1 = &temp;
}
%typemap(argout) int *OUTPUT {
  // return value somehow
}

To make it easier to apply the typemap to different argument types and names, the %apply directive performs a copy of all typemaps from one type to another. For example, if you specify this,

爲了更容易地將類型映射應用於不一樣的參數類型和名稱,%apply 指令將全部類型映射從一種類型複製到另外一種類型。例如,若是你指定此選項,

%apply int *OUTPUT { int *retvalue, int32 *output };

then all of the int *OUTPUT typemaps are copied to int *retvalue and int32 *output.

However, there is a subtle aspect of %apply that needs more description. Namely, %apply does not overwrite a typemap rule if it is already defined for the target datatype. This behavior allows you to do two things:

  • You can specialize parts of a complex typemap rule by first defining a few typemaps and then using %apply to incorporate the remaining pieces.
  • Sets of different typemaps can be applied to the same datatype using repeated %apply directives.

For example:

而後將全部 int *OUTPUT 類型映射覆制到 int *retvalueint32 * output

可是,%apply 有一個細微的方面須要更多描述。就是說,若是此行爲使你能夠作兩件事:

  • 你能夠經過首先定義一些類型映射,而後使用 %apply 來合併其他部分來特化複雜類型映射規則的各個部分。
  • 可使用重複的 %apply 指令將不一樣類型映射的集合應用於相同的數據類型。

例如:

%typemap(in) int *INPUT (int temp) {
  temp = ... get value from $input ...;
  $1 = &temp;
}

%typemap(check) int *POSITIVE {
  if (*$1 <= 0) {
    SWIG_exception(SWIG_ValueError, "Expected a positive number!\n");
    return NULL;
  }
}

...
%apply int *INPUT     { int *invalue };
%apply int *POSITIVE  { int *invalue };

Since %apply does not overwrite or replace any existing rules, the only way to reset behavior is to use the %clear directive. %clear removes all typemap rules defined for a specific datatype. For example:

因爲 %apply 不會覆蓋或替換任何現有規則,所以重置行爲的惟一方法是使用 %clear 僞指令。%clear 刪除爲特定數據類型定義的全部類型映射規則。例如:

%clear int *invalue;

11.15 在類型映射間傳遞數據

It is also important to note that the primary use of local variables is to create stack-allocated objects for temporary use inside a wrapper function (this is faster and less-prone to error than allocating data on the heap). In general, the variables are not intended to pass information between different types of typemaps. However, this can be done if you realize that local names have the argument number appended to them. For example, you could do this:

一樣重要的是要注意,局部變量的主要用途是建立包裝分配的對象,以便在包裝器函數內部臨時使用(與在堆上分配數據相比,此方法更快且更不容易出錯)。一般,這些變量無心在不一樣類型的類型映射之間傳遞信息。可是,若是你意識到局部名稱後面附加了參數編號,則能夠這樣作。例如,你能夠這樣作:

%typemap(in) int *(int temp) {
  temp = (int) PyInt_AsLong($input);
  $1 = &temp;
}

%typemap(argout) int * {
  PyObject *o = PyInt_FromLong(temp$argnum);
  ...
}

In this case, the $argnum variable is expanded into the argument number. Therefore, the code will reference the appropriate local such as temp1 and temp2. It should be noted that there are plenty of opportunities to break the universe here and that accessing locals in this manner should probably be avoided. At the very least, you should make sure that the typemaps sharing information have exactly the same types and names.

在這種狀況下,$argnum 變量將擴展爲參數編號。所以,代碼將引用適當的局部變量,例如 temp1temp2。應當指出,這裏有不少打破宇宙的機會,應該避免以這種方式訪問局部變量。至少,你應該確保共享信息的類型映射具備徹底相同的類型和名稱。

11.16 C++ this 指針

All the rules discussed for typemaps apply to C++ as well as C. However in addition C++ passes an extra parameter into every non-static class method -- the this pointer. Occasionally it can be useful to apply a typemap to this pointer (for example to check and make sure this is non-null before deferencing). Actually, C also has an the equivalent of the this pointer which is used when accessing variables in a C struct.

In order to customise the this pointer handling, target a variable named self in your typemaps. self is the name SWIG uses to refer to the extra parameter in wrapped functions.

For example, if wrapping for Java generation:

討論類型映射的全部規則都適用於 C++ 和 C。可是,此外,C++ 向每一個非靜態類方法(this 指針)傳遞了一個額外的參數。有時候,將類型映射應用於此指針可能會頗有用(例如,檢查並確保在遞延前確保 this 爲非空)。實際上,C 還具備等效於 this 指針的指針,該指針在訪問 C 結構體中的變量時使用。

爲了自定義 this 指針處理,在類型映射中定位一個名爲 self 的變量。self 是 SWIG 在包裝器函數中用來引用附加參數的名稱。

例如,若是包裝爲生成 Java:

%typemap(check) SWIGTYPE *self %{
if (!$1) {
  SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException,
    "invalid native object; delete() likely already called");
  return $null;
}
%}

In the above case, the $1 variable is expanded into the argument name that SWIG is using as the thispointer. SWIG will then insert the check code before the actual C++ class method is called, and will raise an exception rather than crash the Java virtual machine. The generated code will look something like:

在上述狀況下,將 $1 變量擴展爲 SWIG 用做 this 指針的參數名稱。而後,SWIG 將在調用實際的 C++ 類方法以前插入檢查代碼,而且將引起異常而不是使 Java 虛擬機崩潰。生成的代碼以下所示:

if (!arg1) {
SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException,
  "invalid native object; delete() likely already called");
return ;
}
(arg1)->wrappedFunction(...);

Note that if you have a parameter named self then it will also match the typemap. One work around is to create an interface file that wraps the method, but gives the argument a name other than self.

請注意,若是你有一個名爲 self 的參數,則它也將與類型映射匹配。一種解決方法是建立一個包裝該方法的接口文件,但爲自變量指定一個不一樣於 self 的名稱。

11.17 到哪去找更多的信息?

The best place to find out more information about writing typemaps is to look in the SWIG library. Most language modules define all of their default behavior using typemaps. These are found in files such aspython.swg, perl5.swg, tcl8.swg and so forth. The typemaps.i file in the library also contains numerous examples. You should look at these files to get a feel for how to define typemaps of your own. Some of the language modules support additional typemaps and further information is available in the individual chapters for each target language. There you may also find more hands-on practical examples.

要尋找有關編寫類型映射的更多信息,最佳的地點是在 SWIG 庫中。大多數語言模塊都使用類型映射定義全部默認行爲。這些能夠在諸如 python.swgperl5.swgtcl8.swg 等文件中找到。庫中的 typemaps.i 文件也包含許多示例。你應該查看這些文件,以瞭解如何定義本身的類型映射。一些語言模塊支持其餘類型映射,而且在每種章節的每種目標語言中都提供了更多信息。在這裏你還能夠找到更多動手的實際示例。

相關文章
相關標籤/搜索