SWIG 3 中文手冊——6. SWIG 和 C++

6 SWIG 和 C++

This chapter describes SWIG's support for wrapping C++. As a prerequisite, you should first read the chapter SWIG Basics to see how SWIG wraps ANSI C. Support for C++ builds upon ANSI C wrapping and that material will be useful in understanding this chapter.node

本章介紹了 SWIG 對包裝 C++ 的支持。做爲準備條件,你應首先閱讀 SWIG 基礎知識一章,瞭解 SWIG 如何包裝 ANSI C。SWIG 對 C++ 的支持創建在對 ANSI C 包裝的基礎上,並且這部分資料將有助於理解本章。python

6.1 關於包裝 C++

Because of its complexity and the fact that C++ can be difficult to integrate with itself let alone other languages, SWIG only provides support for a subset of C++ features. Fortunately, this is now a rather large subset.c++

In part, the problem with C++ wrapping is that there is no semantically obvious (or automatic ) way to map many of its advanced features into other languages. As a simple example, consider the problem of wrapping C++ multiple inheritance to a target language with no such support. Similarly, the use of overloaded operators and overloaded functions can be problematic when no such capability exists in a target language.git

A more subtle issue with C++ has to do with the way that some C++ programmers think about programming libraries. In the world of SWIG , you are really trying to create binary-level software components for use in other languages. In order for this to work, a "component" has to contain real executable instructions and there has to be some kind of binary linking mechanism for accessing its functionality. In contrast, C++ has increasingly relied upon generic programming and templates for much of its functionality. Although templates are a powerful feature, they are largely orthogonal to the whole notion of binary components and libraries. For example, an STL vector does not define any kind of binary object for which SWIG can just create a wrapper. To further complicate matters, these libraries often utilize a lot of behind the scenes magic in which the semantics of seemingly basic operations (e.g., pointer dereferencing, procedure call, etc.) can be changed in dramatic and sometimes non-obvious ways. Although this "magic" may present few problems in a C++-only universe, it greatly complicates the problem of crossing language boundaries and provides many opportunities to shoot yourself in the foot. You will just have to be careful.程序員

因爲其複雜性以及 C++ 難以將其自身與其餘語言集成的事實,SWIG 僅支持 C++ 功能的一個子集。幸運的是,如今這是一個至關大的子集。數據庫

在某種程度上,C++ 包裝的問題在於沒有語義上明顯(或自動)的方式將其許多高級功能映射到其餘語言。舉個簡單的例子,考慮將 C++ 多繼承包裝到不支持多繼承的目標語言。相似地,當目標語言中不支持重載時,使用重載運算符和重載函數多是有問題的。express

C++ 的一個更微妙的問題與某些 C++ 程序員對程序庫的思考方式有關。在 SWIG 的世界中,你正在嘗試建立用於其餘語言的二進制級軟件組件。爲了使其工做,「組件」必須包含真正的可執行指令,而且必須有某種二進制連接機制來訪問其功能。相比之下,C++ 愈來愈依賴泛型編程和模板來實現其大部分功能。雖然模板是一個強大的功能,但它們在很大程度上與二進制組件和庫的整個概念正交。例如,STL vector 沒有定義任何二進制對象供 SWIG 爲其建立包裝器。爲了使問題進一步複雜化,這些庫一般利用許多幕後魔法,其中看似基本操做(例如,指針解引用、過程調用等)的語義能夠以戲劇性,且有時非顯而易見的方式改變。雖然這種「魔法」可能在 C++ 中只會形成一點問題——但它會使跨語言變得很是複雜,而且可能會使你射到本身的腳(shoot yourself in the foot)。你必需要當心。編程

6.2 方法

To wrap C++, SWIG uses a layered approach to code generation. At the lowest level, SWIG generates a collection of procedural ANSI-C style wrappers. These wrappers take care of basic type conversion, type checking, error handling, and other low-level details of the C++ binding. These wrappers are also sufficient to bind C++ into any target language that supports built-in procedures. In some sense, you might view this layer of wrapping as providing a C library interface to C++. On top of the low-level procedural (flattened) interface, SWIG generates proxy classes that provide a natural object-oriented (OO) interface to the underlying code. The proxy classes are typically written in the target language itself. For instance, in Python, a real Python class is used to provide a wrapper around the underlying C++ object.數組

It is important to emphasize that SWIG takes a deliberately conservative and non-intrusive approach to C++ wrapping. SWIG does not encapsulate C++ classes inside a special C++ adaptor, it does not rely upon templates, nor does it add in additional C++ inheritance when generating wrappers. The last thing that most C++ programs need is even more compiler magic. Therefore, SWIG tries to maintain a very strict and clean separation between the implementation of your C++ application and the resulting wrapper code. You might say that SWIG has been written to follow the principle of least surprise--it does not play sneaky tricks with the C++ type system, it doesn't mess with your class hierarchies, and it doesn't introduce new semantics. Although this approach might not provide the most seamless integration with C++, it is safe, simple, portable, and debuggable.

Some of this chapter focuses on the low-level procedural interface to C++ that is used as the foundation for all language modules. Keep in mind that the target languages also provide the high-level OO interface via proxy classes. More detailed coverage can be found in the documentation for each target language.

爲了包裝 C++ ,SWIG 使用分層方法來生成代碼。在最底層,SWIG 生成一組 ANSI C 程序樣式的包裝器。這些包裝器負責基本類型轉換、類型檢查、錯誤處理以及 C++ 綁定的其餘低級細節。這些包裝器足以將 C++ 綁定到任何支持內置程序的目標語言。從某種意義上說,你能夠將這個包裝層視爲爲 C++ 提供 C 庫接口。在低級程序(扁平化)接口之上,SWIG 生成代理類,爲低級代碼提供天然的面向對象(OO)接口。代理類一般用目標語言自己編寫。例如,在 Python 中,一個真正的 Python 類用於提供底層 C++ 對象的包裝器。

重要的是要強調 SWIG 採用故意保守且非侵入性的 C++ 包裝方法。SWIG 不會將 C++ 類封裝在特殊的 C++ 適配器中,它不依賴於模板,也不會在生成包裝器時添加額外的 C++ 繼承。大多數 C++ 程序須要的最後一件事就是編譯魔法。所以,SWIG 嘗試在 C++ 應用程序的實現和生成的包裝器代碼之間保持很是嚴格和清晰的分離。你可能會說 SWIG 的編寫遵循最少驚喜的原則——它不會與 C++ 類型系統一塊兒玩狡猾的技巧,它不會破壞你的類層次結構,而且它不會引入新的語義。雖然這種方法可能沒法提供與 C++ 最無縫的集成,但它是安全、簡單、可移植和可調試的。

本章的一些內容側重於 C++ 的低級程序接口,做爲全部語言模塊的基礎使用。請記住,目標語言還經過代理類提供高級 OO 接口。能夠在每種目標語言的文檔中找到更詳細的介紹。

6.3 支持的 C++ 功能

SWIG currently supports most C++ features including the following:

SWIG 目前支持的 C++ 功能以下:

  • Classes
  • Constructors and destructors
  • Virtual functions
  • Public inheritance (including multiple inheritance)
  • Static functions
  • Function and method overloading
  • Operator overloading for many standard operators
  • References
  • Templates (including specialization and member templates)
  • Pointers to members
  • Namespaces
  • Default parameters
  • Smart pointers

The following C++ features are not currently supported:

  • Overloaded versions of certain operators (new, delete, etc.)

As a rule of thumb, SWIG should not be used on raw C++ source files, use header files only.

SWIG's C++ support is an ongoing project so some of these limitations may be lifted in future releases. However, we make no promises. Also, submitting a bug report is a very good way to get problems fixed (wink).

目前不支持下列 C++ 功能:

  • 某些運算符的重載(newdelete 等)

根據經驗,SWIG 不該該用於原始 C++ 源文件,只能使用頭文件。

SWIG 的 C++ 支持是一個持續的項目,所以在將來的版本中可能會解除其中一些限制。可是,咱們不作任何承諾。此外,提交錯誤報告是解決問題的一個很是好的方法(眨眨眼)。

6.4 命令行選項與編譯

When wrapping C++ code, it is critical that SWIG be called with the -c++ option. This changes the way a number of critical features such as memory management are handled. It also enables the recognition of C++ keywords. Without the -c++ flag, SWIG will either issue a warning or a large number of syntax errors if it encounters C++ code in an interface file.

When compiling and linking the resulting wrapper file, it is normal to use the C++ compiler. For example:

在包裝 C++ 代碼時,使用 -c++ 選項調用 SWIG 相當重要。這改變了處理許多關鍵功能的方式,如內存管理等。它還能夠識別 C++ 關鍵字。若是沒有 -c++ 標誌,若是 SWIG 在接口文件中遇到 C++ 代碼,它將發出警告或大量語法錯誤。

在編譯和連接生成的包裝器文件時,一般使用 C++ 編譯器。例如:

$ swig -c++ -tcl example.i
$ c++ -fPIC -c example_wrap.cxx
$ c++ example_wrap.o $(OBJS) -o example.so

Unfortunately, the process varies slightly on each platform. Make sure you refer to the documentation on each target language for further details. The SWIG Wiki also has further details.

Compatibility Note: Early versions of SWIG generated just a flattened low-level C style API to C++ classes by default. The commandline option is recognised by many target languages and will generate just this interface as in earlier versions.

In order to provide a natural mapping from C++ classes to the target language classes, SWIG's target languages mostly wrap C++ classes with special proxy classes. These proxy classes are typically implemented in the target language itself. For example, if you're building a Python module, each C++ class is wrapped by a Python proxy class. Or if you're building a Java module, each C++ class is wrapped by a Java proxy class.

不幸的是,每一個操做系統的流程略有不一樣。請務必參閱每種目標語言的文檔以獲取更多詳細信息。SWIG Wiki 還有更多細節。

注意兼容性:默認狀況下,早期版本的 SWIG 只爲 C++ 類生成了扁平的低級 C 風格 API。命令行選項被許多目標語言識別,而且將像早期版本同樣生成此接口。

爲了提供從 C++ 類到目標語言類的天然映射,SWIG 的目標語言主要用特殊的代理類包裝 C++ 類。這些代理類一般以目標語言自己實現。例如,若是你正在構建 Python 模塊,則每一個 C++ 類都由 Python 代理類包裝。或者,若是你正在構建 Java 模塊,則每一個 C++ 類都由 Java 代理類包裝。

6.5.1 代理類的構造

Proxy classes are always constructed as an extra layer of wrapping that uses low-level accessor functions. To illustrate, suppose you had a C++ class like this:

代理類始終構造爲使用低級訪問器函數的額外包裝層。爲了說明這一點,假設你有一個這樣的 C++ 類:

class Foo {
  public:
    Foo();
    ~Foo();
    int  bar(int x);
    int  x;
};

Using C++ as pseudocode, a proxy class looks something like this:

使用 C++ 做爲僞代碼,代理類看起來像這樣:

class FooProxy {
  private:
    Foo    *self;
  public:
    FooProxy() {
      self = new_Foo();
    }
    ~FooProxy() {
      delete_Foo(self);
    }
    int bar(int x) {
      return Foo_bar(self, x);
    }
    int x_get() {
      return Foo_x_get(self);
    }
    void x_set(int x) {
      Foo_x_set(self, x);
    }
};

Of course, always keep in mind that the real proxy class is written in the target language. For example, in Python, the proxy might look roughly like this:

固然,請記住,真正的代理類是用目標語言編寫的。例如,在 Python 中,代理可能看起來大體以下:

class Foo:
    def __init__(self):
        self.this = new_Foo()
    def __del__(self):
        delete_Foo(self.this)
    def bar(self, x):
        return Foo_bar(self.this, x)
    def __getattr__(self, name):
        if name == 'x':
            return Foo_x_get(self.this)
        ...
    def __setattr__(self, name, value):
        if name == 'x':
            Foo_x_set(self.this, value)
        ...

Again, it's important to emphasize that the low-level accessor functions are always used by the proxy classes. Whenever possible, proxies try to take advantage of language features that are similar to C++. This might include operator overloading, exception handling, and other features.

一樣,重點強調代理類老是使用低級訪問器函數。只要有可能,代理類就會嘗試利用與 C++ 相似的語言功能。這可能包括運算符重載、異常處理和其餘功能。

6.5.2 代理類中的資源管理

A major issue with proxies concerns the memory management of wrapped objects. Consider the following C++ code:

代理類的主要問題涉及對包裝對象的內存管理。考慮如下 C++ 代碼:

class Foo {
public:
  Foo();
  ~Foo();
  int bar(int x);
  int x;
};

class Spam {
public:
  Foo *value;
  ...
};

Consider some script code that uses these classes:

考慮使用這些類的腳本代碼:

f = Foo()               # Creates a new Foo
s = Spam()              # Creates a new Spam
s.value = f             # Stores a reference to f inside s
g = s.value             # Returns stored reference
g = 4                   # Reassign g to some other value
del f                   # Destroy f

Now, ponder the resulting memory management issues. When objects are created in the script, the objects are wrapped by newly created proxy classes. That is, there is both a new proxy class instance and a new instance of the underlying C++ class. In this example, both f and s are created in this way. However, the statement s.value is rather curious---when executed, a pointer to f is stored inside another object. This means that the scripting proxy class AND another C++ class share a reference to the same object. To make matters even more interesting, consider the statement g = s.value. When executed, this creates a new proxy class g that provides a wrapper around the C++ object stored in s.value. In general, there is no way to know where this object came from---it could have been created by the script, but it could also have been generated internally. In this particular example, the assignment of g results in a second proxy class for f. In other words, a reference to f is now shared by two proxy classes and a C++ class.

Finally, consider what happens when objects are destroyed. In the statement, g=4, the variable g is reassigned. In many languages, this makes the old value of g available for garbage collection. Therefore, this causes one of the proxy classes to be destroyed. Later on, the statement del f destroys the other proxy class. Of course, there is still a reference to the original object stored inside another C++ object. What happens to it? Is the object still valid?

To deal with memory management problems, proxy classes provide an API for controlling ownership. In C++ pseudocode, ownership control might look roughly like this:

如今,思考由此產生的內存管理問題。在腳本中建立對象時,對象將由新建立的代理類包裝。也就是說,既有新的代理類實例又有底層 C++ 類的新實例。在這個例子中,fs 都是以這種方式建立的。可是,語句 s.value 至關奇怪——執行時,指向 f 的指針存儲在另外一個對象中。這意味着腳本代理類以及另外一個 C++ 類共享對同一對象的引用。爲了使事情變得更有趣,請考慮語句 g = s.value。執行時,這將建立一個新的代理類對象 g,它提供了存儲在 s.value 中的 C++ 對象的包裝器。一般,沒法知道此對象的來源——它多是由腳本建立的,但也多是在內部生成的。在這個特定的例子中,g 的賦值致使了 f 的第二個代理類對象。換句話說,對 f 的引用如今由兩個代理類以及一個 C++ 類共享。

最後,考慮一下對象被銷燬時會發生什麼。在聲明中,g = 4,變量 g 被從新分配。在許多語言中,這使得 g 的舊值可被垃圾收集。所以,這會致使其中一個代理類對象被銷燬。後來,語句 del f 銷燬了另外一個代理類。固然,仍然存在對存儲在另外一個 C++ 對象中的原始對象的引用。怎麼回事?對象仍然有效嗎?

爲了處理內存管理問題,代理類提供了用於控制全部權的 API。在 C++ 僞代碼中,全部權控制可能看起來大體以下:

class FooProxy {
  public:
    Foo *self;
    int thisown;

    FooProxy() {
      self = new_Foo();
      thisown = 1;       // Newly created object
    }
    ~FooProxy() {
      if (thisown) delete_Foo(self);
    }
    ...
    // Ownership control API
    void disown() {
      thisown = 0;
    }
    void acquire() {
      thisown = 1;
    }
};

class FooPtrProxy: public FooProxy {
public:
  FooPtrProxy(Foo *s) {
    self = s;
    thisown = 0;
  }
};

class SpamProxy {
  ...
  FooProxy *value_get() {
    return FooPtrProxy(Spam_value_get(self));
  }
  void value_set(FooProxy *v) {
    Spam_value_set(self, v->self);
    v->disown();
  }
  ...
};

Looking at this code, there are a few central features:

  • Each proxy class keeps an extra flag to indicate ownership. C++ objects are only destroyed if the ownership flag is set.
  • When new objects are created in the target language, the ownership flag is set.
  • When a reference to an internal C++ object is returned, it is wrapped by a proxy class, but the proxy class does not have ownership.
  • In certain cases, ownership is adjusted. For instance, when a value is assigned to the member of a class, ownership is lost.
  • Manual ownership control is provided by special disown() and acquire() methods.

Given the tricky nature of C++ memory management, it is impossible for proxy classes to automatically handle every possible memory management problem. However, proxies do provide a mechanism for manual control that can be used (if necessary) to address some of the more tricky memory management problems.

查看此代碼,有一些核心功能:

  • 每一個代理類都有一個額外的標誌來表示全部權。只有設置了全部權標誌,纔會銷燬 C++ 對象。
  • 以目標語言建立新對象時,將設置全部權標誌。
  • 當返回對內部 C++ 對象的引用時,它由代理類包裝,但代理類沒有全部權。
  • 在某些狀況下,全部權會進行調整。例如,將值分配給類的成員時,全部權將丟失。
  • 手動全部權控制由特殊的 disown()acquire() 方法提供。

鑑於 C++ 內存管理的棘手性,代理類沒法自動處理每一個可能的內存管理問題。可是,代理確實提供了一種手動控制機制,能夠用(若是須要)來解決一些更棘手的內存管理問題。

6.5.3 語言特定的細節

Language specific details on proxy classes are contained in the chapters describing each target language. This chapter has merely introduced the topic in a very general way.

代理類的語言特定詳細信息包含在描述每種目標語言的章節中。本章僅以很是歸納的方式介紹該主題。

6.6 簡單 C++ 包裝

The following code shows a SWIG interface file for a simple C++ class.

下面的代碼顯示了一個簡單 C++ 類的 SWIG 接口文件。

%module list
%{
#include "list.h"
%}

// Very simple C++ example for linked list

class List {
public:
  List();
  ~List();
  int  search(char *value);
  void insert(char *);
  void remove(char *);
  char *get(int n);
  int  length;
static void print(List *l);
};

To generate wrappers for this class, SWIG first reduces the class to a collection of low-level C-style accessor functions which are then used by the proxy classes.

要爲此類生成包裝器,SWIG 首先將類降級爲低級 C 風格訪問器函數的集合,而後由代理類使用。

6.6.1 構造函數和析構函數

C++ constructors and destructors are translated into accessor functions such as the following :

C++ 構造函數和析構函數被翻譯成以下訪問器函數:

List * new_List(void) {
  return new List;
}
void delete_List(List *l) {
  delete l;
}

6.6.2 默認構造函數、拷貝構造函數和隱式析構函數

Following the C++ rules for implicit constructor and destructors, SWIG will automatically assume there is one even when they are not explicitly declared in the class interface.

In general then:

  • If a C++ class does not declare any explicit constructor, SWIG will automatically generate a wrapper for one.
  • If a C++ class does not declare an explicit copy constructor, SWIG will automatically generate a wrapper for one if the %copyctor is used.
  • If a C++ class does not declare an explicit destructor, SWIG will automatically generate a wrapper for one.

And as in C++, a few rules that alters the previous behavior:

  • A default constructor is not created if a class already defines a constructor with arguments.
  • Default constructors are not generated for classes with pure virtual methods or for classes that inherit from an abstract class, but don't provide definitions for all of the pure methods.
  • A default constructor is not created unless all base classes support a default constructor.
  • Default constructors and implicit destructors are not created if a class defines them in a private or protected section.
  • Default constructors and implicit destructors are not created if any base class defines a non-public default constructor or destructor.

SWIG should never generate a default constructor, copy constructor or default destructor wrapper for a class in which it is illegal to do so. In some cases, however, it could be necessary (if the complete class declaration is not visible from SWIG , and one of the above rules is violated) or desired (to reduce the size of the final interface) by manually disabling the implicit constructor/destructor generation.

To manually disable these, the %nodefaultctor and %nodefaultdtor feature flag directives can be used. Note that these directives only affects the implicit generation, and they have no effect if the default/copy constructors or destructor are explicitly declared in the class interface.

For example:

遵循 C++ 的隱式構造函數和析構函數規則,即便未在類接口中顯式聲明它們,SWIG 也會自動假設存在一個。

通常來講:

  • 若是 C++ 類沒有聲明任何顯式構造函數,SWIG 將自動生成一個包裝器。
  • 若是 C++ 類沒有聲明顯式拷貝構造函數,若是使用 %copyctor,SWIG 將自動生成一個包裝器。
  • 若是 C++ 類沒有聲明顯式析構函數,SWIG 將自動生成一個包裝器。

就像在 C++ 中同樣,有一些改變上述行爲的規則:

  • 若是類已經定義了帶參數的構造函數,則不會建立默認構造函數。
  • 不爲具備純虛方法的類,或從抽象類繼承的類生成默認構造函數,但不爲全部純虛方法提供定義。
  • 除非全部基類都支持默認構造函數,不然不會建立默認構造函數。
  • 若是類在 privateprotected 部分中定義默認構造函數和隱式析構函數,則不會建立它們。
  • 若是任何基類定義非公有默認構造函數或析構函數,則不會建立默認構造函數和隱式析構函數。

SWIG 永遠不該該爲一個類非法生成默認構造函數、拷貝構造函數或默認析構函數包裝器。可是,在某些狀況下,可能有必要(若是從 SWIG 看不到完整的類聲明,而且違反了上述規則之一)或者但願(經過手動禁用隱式構造函數來減少最終接口的大小)析構函數生成。

要手動禁用這些,可使用 %nodefaultctor%nodefaultdtor 功能標誌指令。請注意,這些指令僅影響隱式生成,若是在類接口中顯式聲明瞭默認、拷貝構造函數或析構函數,則它們不起做用。

例如:

%nodefaultctor Foo;  // Disable the default constructor for class Foo.
class Foo {          // No default constructor is generated, unless one is declared
...
};
class Bar {          // A default constructor is generated, if possible
...
};

The directive %nodefaultctor can also be applied "globally", as in:

%nodefaultctor 指令能夠「全局」使用,例如:

%nodefaultctor; // Disable creation of default constructors
class Foo {     // No default constructor is generated, unless one is declared
...
};
class Bar {
public:
  Bar();        // The default constructor is generated, since one is declared
};
%clearnodefaultctor; // Enable the creation of default constructors again

The corresponding %nodefaultdtor directive can be used to disable the generation of the default or implicit destructor, if needed. Be aware, however, that this could lead to memory leaks in the target language. Hence, it is recommended to use this directive only in well known cases. For example:

若是須要,相應的 %nodefaultdtor 指令可用於禁用默認或隱式析構函數的生成。但請注意,這可能會致使目標語言中的內存泄漏。所以,建議僅在充分了解的狀況下使用此指令。例如:

%nodefaultdtor Foo;   // Disable the implicit/default destructor for class Foo.
class Foo {           // No destructor is generated, unless one is declared
...
};

Compatibility Note: The generation of default constructors/implicit destructors was made the default behavior in SWIG 1.3.7. This may break certain older modules, but the old behavior can be easily restored using %nodefault or the -nodefault command line option. Furthermore, in order for SWIG to properly generate (or not generate) default constructors, it must be able to gather information from both the private andprotected sections (specifically, it needs to know if a private or protected constructor/destructor is defined). In older versions of SWIG , it was fairly common to simply remove or comment out the private and protected sections of a class due to parser limitations. However, this removal may now cause SWIG to erroneously generate constructors for classes that define a constructor in those sections. Consider restoring those sections in the interface or using %nodefault to fix the problem.

Note: The %nodefault directive/-nodefault options described above, which disable both the default constructor and the implicit destructors, could lead to memory leaks, and so it is strongly recommended to not use them.

注意兼容性:默認構造函數、隱式析構函數的生成是 SWIG 1.3.7 中的默認行爲。這可能會破壞某些舊模塊,但可使用 %nodefault-nodefault 命令行選項輕鬆恢復舊行爲。此外,爲了使 SWIG 正確生成(或不生成)默認構造函數,它必須可以從 privateprotected 部分收集信息(具體來講,它須要知道私有或保護的構造函數、析構函數是否被定義)。在舊版本的 SWIG 中,因爲解析器的限制,簡單地刪除或註釋掉類的私有和保護部分是至關常見的。可是,刪除行爲如今可能致使 SWIG 錯誤地爲在這些部分中定義構造函數的類生成構造函數。考慮在接口中恢復這些部分或使用 %nodefault 來解決問題。

注意:上面描述和 %nodefault 指令和 -nodefault 選項會禁用默認構造函數和隱式析構函數,可能致使內存泄漏,所以強烈建議不要使用它們。

6.6.3 當不能建立構造函數包裝器時

If a class defines a constructor, SWIG normally tries to generate a wrapper for it. However, SWIG will not generate a constructor wrapper if it thinks that it will result in illegal wrapper code. There are really two cases where this might show up.

First, SWIG won't generate wrappers for protected or private constructors. For example:

若是一個類定義了一個構造函數,SWIG 一般會嘗試爲它生成一個包裝器。可是,若是 SWIG 認爲它將致使非法的包裝器代碼,它將不會生成構造函數包裝器。有兩種可能會出現這種狀況。

首先,SWIG 不會爲保護或私有構造函數生成包裝器。例如:

class Foo {
protected:
  Foo();         // Not wrapped.
public:
  ...
};

Next, SWIG won't generate wrappers for a class if it appears to be abstract--that is, it has undefined pure virtual methods. Here are some examples:

接下來,若是某個相似乎是抽象的,它將不會爲類生成包裝器——也就是說,它具備未定義的純虛方法。這裏有些例子:

class Bar {
public:
  Bar();               // Not wrapped.  Bar is abstract.
  virtual void spam(void) = 0;
};

class Grok : public Bar {
public:
  Grok();            // Not wrapped. No implementation of abstract spam().
};

Some users are surprised (or confused) to find missing constructor wrappers in their interfaces. In almost all cases, this is caused when classes are determined to be abstract. To see if this is the case, run SWIG with all of its warnings turned on:

一些用戶爲在他們的接口中找不到構造函數包裝器感到驚訝(或困惑)。幾乎在全部狀況下,這都是在肯定類是抽象類時引發的。要查看是不是這種狀況,請運行 SWIG 並打開其全部警告:

% swig -Wall -python module.i

In this mode, SWIG will issue a warning for all abstract classes. It is possible to force a class to be non-abstract using this:

在此模式下,SWIG 將爲全部抽象類發出警告。可使用如下方法強制類爲非抽象類:

%feature("notabstract") Foo;

class Foo : public Bar {
public:
  Foo();    // Generated no matter what---not abstract.
  ...
};

More information about %feature can be found in the Customization features chapter.

更多關於 %feature 的信息能夠在自定義功能章節找到。

6.6.4 拷貝構造函數

If a class defines more than one constructor, its behavior depends on the capabilities of the target language. If overloading is supported, the copy constructor is accessible using the normal constructor function. For example, if you have this:

若是一個類定義了多個構造函數,則其行爲取決於目標語言的功能。若是支持重載,則可使用常規構造函數訪問拷貝構造函數。例如,若是你有這個:

class List {
public:
    List();
    List(const List &);      // Copy constructor
    ...
};

then the copy constructor can be used as follows:

拷貝構造函數能夠以下使用:

x = List()               # Create a list
y = List(x)              # Copy list x

If the target language does not support overloading, then the copy constructor is available through a special function like this:

若是目標語言不支持重載,則能夠經過以下特殊函數使用拷貝構造函數:

List *copy_List(List *f) {
    return new List(*f);
}

Note: For a class X, SWIG only treats a constructor as a copy constructor if it can be applied to an object of type X or X *. If more than one copy constructor is defined, only the first definition that appears is used as the copy constructor--other definitions will result in a name-clash. Constructors such as X(const X &), X(X &), and X(X *) are handled as copy constructors in SWIG .

Note: SWIG does not generate a copy constructor wrapper unless one is explicitly declared in the class. This differs from the treatment of default constructors and destructors. However, copy constructor wrappers can be generated if using the copyctor feature flag. For example:

注意:對於類 X,若是應用於類型爲 XX * 的對象,則 SWIG 僅將構造函數視爲拷貝構造函數。若是定義了多個拷貝構造函數,則只有出現的第一個定義用做拷貝構造函數——其餘定義將致使名稱衝突。諸如 X(const X &)X(X &)X(X *) 之類的構造函數在 SWIG 中都做爲拷貝構造函數處理。

注意: SWIG 確實生成拷貝構造函數包裝器,除非在類中顯式聲明瞭一個。這與默認構造函數和析構函數的處理不一樣。可是,若是使用 copyctor 功能標誌,則能夠生成拷貝構造函數包裝器。例如:

%copyctor List;

class List {
public:
    List();
};

Will generate a copy constructor wrapper for List.

Compatibility note: Special support for copy constructors was not added until SWIG-1.3.12. In previous versions, copy constructors could be wrapped, but they had to be renamed. For example:

將爲 List 生成一個拷貝構造函數包裝器。

注意兼容性:直到 SWIG-1.3.12 才添加對拷貝構造函數的特殊支持。在之前的版本中,能夠包裝拷貝構造函數,但必須重命名它們。例如:

class Foo {
public:
  Foo();
  %name(CopyFoo) Foo(const Foo &);
  ...
};

For backwards compatibility, SWIG does not perform any special copy-constructor handling if the constructor has been manually renamed. For instance, in the above example, the name of the constructor is set to new_CopyFoo(). This is the same as in older versions.

爲了向後兼容,若是已手動重命名構造函數,SWIG 不會執行任何特殊的拷貝構造函數處理。例如,在上面的例子中,構造函數的名稱被設置爲 new_CopyFoo()。這與舊版本相同。

6.6.5 成員函數

All member functions are roughly translated into accessor functions like this :

全部成員函數大體翻譯成這樣的訪問器函數:

int List_search(List *obj, char *value) {
  return obj->search(value);
}

This translation is the same even if the member function has been declared as virtual.

It should be noted that SWIG does not actually create a C accessor function in the code it generates. Instead, member access such asobj->search(value) is directly inlined into the generated wrapper functions. However, the name and calling convention of the low-level procedural wrappers match the accessor function prototype described above.

即便成員函數已聲明爲 virtual,此轉換也是相同的。

應該注意的是,SWIG 實際上並無在它生成的代碼中建立一個 C 訪問器函數。相反,成員訪問(如 obj->search(value))直接內聯到生成的包裝器函數中。可是,低級程序包裝器的名稱和調用約定與上述的訪問器函數原型匹配。

6.6.6 靜態成員

Static member functions are called directly without making any special transformations. For example, the static member functionprint(List *l) directly invokes List::print(List *l) in the generated wrapper code.

直接調用靜態成員函數而不進行任何特殊轉換。例如,靜態成員函數 print(List *l) 直接在生成的包裝器代碼中調用 List::print(List *l)

6.6.7 成員數據

Member data is handled in exactly the same manner as for C structures. A pair of accessor functions are effectively created. For example :

成員數據的處理方式與 C 結構體徹底相同。有效地建立了一對存取器函數。例如:

int List_length_get(List *obj) {
  return obj->length;
}
int List_length_set(List *obj, int value) {
  obj->length = value;
  return value;
}

A read-only member can be created using the %immutable and %mutable feature flag directive. For example, we probably wouldn't want the user to change the length of a list so we could do the following to make the value available, but read-only.

可使用 %immutable%mutable 功能標誌指令建立只讀成員。例如,咱們可能不但願用戶更改列表的長度,所以咱們能夠執行如下操做以使值可用,可是隻讀。

class List {
public:
...
%immutable;
  int length;
%mutable;
...
};

Alternatively, you can specify an immutable member in advance like this:

或者,你能夠提早指定不可變成員,以下所示:

%immutable List::length;
...
class List {
  ...
  int length;         // Immutable by above directive
  ...
};

Similarly, all data attributes declared as const are wrapped as read-only members.

By default, SWIG uses the const reference typemaps for members that are primitive types. There are some subtle issues when wrapping data members that are not primitive types, such as classes. For instance, if you had another class like this,

相似地,聲明爲 const 的全部數據屬性都包裝爲只讀成員。

默認狀況下,SWIG 將常引用類型映射用於基本類型的成員。在包裝非基本類型的數據成員(例如類)時,存在一些微妙的問題。例如,若是你有另外一個這樣的類,

class Foo {
public:
    List items;
    ...

then the low-level accessor to the items member actually uses pointers. For example:

而後 items 成員的低級訪問器實際上使用指針。例如:

List *Foo_items_get(Foo *self) {
    return &self->items;
}
void Foo_items_set(Foo *self, List *value) {
    self->items = *value;
}

More information about this can be found in the SWIG Basics chapter, Structure data members section.

The wrapper code to generate the accessors for classes comes from the pointer typemaps. This can be somewhat unnatural for some types. For example, a user would expect the STL std::string class member variables to be wrapped as a string in the target language, rather than a pointer to this class. The const reference typemaps offer this type of marshalling, so there is a feature to tell SWIG to use the const reference typemaps rather than the pointer typemaps. It is the naturalvar feature and can be used to effectively change the way accessors are generated to the following:

有關這方面的更多信息,請參閱 SWIG 基礎知識章節結構體數據成員部分。

生成類訪問器的包裝器代碼來自指針類型映射。對於某些類型,這可能有點不天然。例如,用戶但願將 STL std::string 類成員變量包裝爲目標語言中的字符串,而不是指向此類的指針。常引用類型映射提供了這種類型的編組,所以有一個功能能夠告訴 SWIG 使用常引用類型映射而不是指針類型映射。它是 naturalvar 功能,可用於有效地將訪問器的生成方式,例如:

const List &Foo_items_get(Foo *self) {
    return self->items;
}
void Foo_items_set(Foo *self, const List &value) {
    self->items = value;
}

The %naturalvar directive is a macro for, and hence equivalent to, %feature("naturalvar"). It can be used as follows:

%naturalvar 指令是一個宏,所以至關於 %feature("naturalvar")。它能夠以下使用:

// All List variables will use const List& typemaps
%naturalvar List;

// Only Foo::myList will use const List& typemaps
%naturalvar Foo::myList;
struct Foo {
  List myList;
};

// All non-primitive types will use const reference typemaps
%naturalvar;

The observant reader will notice that %naturalvar works like any other feature flag directive but with some extra flexibility. The first of the example usages above shows %naturalvar attaching to the myList's variable type, that is the List class. The second usage shows %naturalvar attaching to the variable name. Hence the naturalvar feature can be used on either the variable's name or type. Note that using the naturalvar feature on a variable's name overrides any naturalvar feature attached to the variable's type.

It is generally a good idea to use this feature globally as the reference typemaps have extra NULL checking compared to the pointer typemaps. A pointer can be NULL, whereas a reference cannot, so the extra checking ensures that the target language user does not pass in a value that translates to a NULL pointer and thereby preventing any potential NULL pointer dereferences. The %naturalvar feature will apply to global variables in addition to member variables in some language modules, eg C# and Java.

The naturalvar behavior can also be turned on as a global setting via the -naturalvar commandline option or the module mode option, %module(naturalvar=1). However, any use of %feature("naturalvar") will override the global setting.

Compatibility note: The %naturalvar feature was introduced in SWIG-1.3.28, prior to which it was necessary to manually apply the const reference typemaps, eg %apply const std::string & { std::string * }, but this example would also apply the typemaps to methods taking a std::string pointer.

Compatibility note: Read-only access used to be controlled by a pair of directives %readonly and %readwrite. Although these directives still work, they generate a warning message. Simply change the directives to %immutable; and %mutable; to silence the warning. Don't forget the extra semicolon!

Compatibility note: Prior to SWIG-1.3.12, all members of unknown type were wrapped into accessor functions using pointers. For example, if you had a structure like this

細心的讀者會注意到 %naturalvar 與任何其餘功能標誌指令同樣,但具備一些額外的靈活性。上面的第一個示例用法顯示 %naturalvar 附加到 myList 的變量類型,即 List 類。第二種用法顯示 %naturalvar 附加到變量名稱。所以,naturalvar 功能可用於變量的名稱或類型。請注意,在變量名稱上使用 naturalvar 功能會覆蓋附加到變量類型的任何 naturalvar 功能。

全局使用此功能一般是個好主意,由於與指針類型映射相比,引用類型映射具備額外的 NULL 檢查。指針能夠爲 NULL,而引用則不能,所以額外檢查可確保目標語言用戶不會傳入轉換爲 NULL 指針的值,從而防止任何可能的 NULL 指針解引用。除了一些語言模塊中的成員變量以外,%naturalvar 功能還將應用於全局變量,例如在 C# 和 Java 中。

天然變換行爲也能夠經過 -naturalvar 命令行選項或模塊模式選項 %module(naturalvar = 1) 做爲全局設置打開。可是,任何使用 %feature("naturalvar") 都將覆蓋全局設置。

注意兼容性:SWIG-1.3.28 中引入了 %naturalvar 功能,在此以前必須手動應用常引用類型映射,例如 %apply const std::string&{std::string *},可是這個例子也將類型映射應用於帶有 std::string 指針的方法。

注意兼容性:只讀訪問過去由一對指令 %readonly%readwrite 控制。儘管這些指令仍然有效,但它們會生成警告消息。只需將指令更改成 %immutable;%mutable; 便可使警告靜音。不要忘記額外的分號!

注意兼容性:在 SWIG-1.3.12以前,全部未知類型的成員都使用指針包裝到訪問器函數中。例如,若是你有這樣的結構體

struct Foo {
  size_t  len;
};

and nothing was known about size_t, then accessors would be written to work with size_t *. Starting in SWIG-1.3.12, this behavior has been modified. Specifically, pointers will only be used if SWIG knows that a datatype corresponds to a structure or class. Therefore, the above code would be wrapped into accessors involving size_t. This change is subtle, but it smooths over a few problems related to structure wrapping and some of SWIG's customization features.

而且對 size_t 一無所知,而後將使用 size_t * 來編寫訪問器。從 SWIG-1.3.12 開始,此行爲已被修改。具體來講,若是 SWIG 知道數據類型對應於結構體或類,則僅使用指針。所以,上面的代碼將被包裝到涉及 size_t 的訪問器中。這種變化是微妙的,但它平滑了有關結構體包裝和 SWIG 自定義功能的一些問題。

6.7 默認參數

SWIG will wrap all types of functions that have default arguments. For example member functions:

SWIG 將包裝具備默認參數的全部類型的函數。例如成員函數:

class Foo {
public:
    void bar(int x, int y = 3, int z = 4);
};

SWIG handles default arguments by generating an extra overloaded method for each defaulted argument. SWIG is effectively handling methods with default arguments as if it was wrapping the equivalent overloaded methods. Thus for the example above, it is as if we had instead given the following to SWIG :

SWIG 經過爲每一個默認參數生成額外的重載方法來處理默認參數。SWIG 有效地處理正在使用默認參數的方法,就好像它包裝了等效的重載方法同樣。所以,對於上面的示例,就好像咱們已經向 SWIG 提供瞭如下內容:

class Foo {
public:
    void bar(int x, int y, int z);
    void bar(int x, int y);
    void bar(int x);
};

The wrappers produced are exactly the same as if the above code was instead fed into SWIG . Details of this are covered later in the Wrapping Overloaded Functions and Methods section. This approach allows SWIG to wrap all possible default arguments, but can be verbose. For example if a method has ten default arguments, then eleven wrapper methods are generated.

Please see the Features and default arguments section for more information on using %feature with functions with default arguments. The Ambiguity resolution and renaming section also deals with using %rename and %ignore on methods with default arguments. If you are writing your own typemaps for types used in methods with default arguments, you may also need to write a typecheck typemap. See the Typemaps and overloading section for details or otherwise use the compactdefaultargs feature flag as mentioned below.

Compatibility note: Versions of SWIG prior to SWIG-1.3.23 wrapped default arguments slightly differently. Instead a single wrapper method was generated and the default values were copied into the C++ wrappers so that the method being wrapped was then called with all the arguments specified. If the size of the wrappers are a concern then this approach to wrapping methods with default arguments can be re-activated by using the compactdefaultargsfeature flag.

生成的包裝器與上面的代碼經過 SWIG 獲得的徹底相同。稍後將在包裝重載函數與方法部分中介紹其詳細信息。這種方法容許 SWIG 包裝全部可能的默認參數,但多是冗長的。例如,若是方法有十個默認參數,則會生成十一個包裝器方法。

有關將 %feature 與具備默認參數的函數一塊兒使用的更多信息,請參閱功能和默認參數部分。消歧義和重命名部分還討論了對具備默認參數的方法使用 %rename%ignore。若是你正在爲具備默認參數的方法中使用的類型編寫本身的類型映射,則可能還須要編寫 typecheck 類型映射。有關詳細信息,請參閱類型映射與重載部分,不然請使用下面提到和 compactdefaultargs 功能標誌。

注意兼容性: SWIG-1.3.23 與以前的 SWIG 版本包含的默認參數略有不一樣,而是生成了一個包裝器方法,並將默認值複製到 C++ 包裝器中,以便隨後使用指定的全部參數調用包裝的方法。若是包裝器的大小是一個問題,那麼使用 compactdefaultargs 功能標誌能夠從新激活這種使用默認參數包裝方法的方法。

%feature("compactdefaultargs") Foo::bar;
class Foo {
public:
    void bar(int x, int y = 3, int z = 4);
};

This is great for reducing the size of the wrappers, but the caveat is it does not work for the statically typed languages, such as C# and Java, which don't have optional arguments in the language, Another restriction of this feature is that it cannot handle default arguments that are not public. The following example illustrates this:

這對於減少包裝器的大小很是有用,但須要注意的是它不適用於靜態類型語言,例如 C# 和 Java,它們在語言中沒有可選參數。此功能的另外一個限制是它沒法處理非公有的默認參數。如下示例說明這一點:

class Foo {
private:
  static const int spam;
public:
  void bar(int x, int y = spam);   // Won't work with %feature("compactdefaultargs") -
                                   // private default value
};

This produces uncompilable wrapper code because default values in C++ are evaluated in the same scope as the member function whereas SWIG evaluates them in the scope of a wrapper function (meaning that the values have to be public).

The compactdefaultargs feature is automatically turned on when wrapping C code with default arguments. Some target languages will also automatically turn on this feature if the keyword arguments feature (kwargs) is specified for either C or C++ functions, and the target language supports kwargs, the compactdefaultargs feature is also automatically turned on. Keyword arguments are a language feature of some scripting languages, for example Ruby and Python. SWIG is unable to support kwargs when wrapping overloaded methods, so the default approach cannot be used.

這會產生沒法編譯的包裝器代碼,由於 C++ 中的默認值是在與成員函數相同的範圍內計算的,而 SWIG 在包裝函數的範圍內賦值它們(意味着值必須是公有的)。

在包裝帶有默認參數的 C 代碼時,會自動打開 compactdefaultargs 功能。若是爲 C 或 C++ 函數指定了關鍵字參數功能(kwargs),而且目標語言支持 kwargs,則某些目標語言也將自動打開此功能,compactdefaultargs 功能也會自動打開。關鍵字參數是某些腳本語言的語言特性,例如 Ruby 和 Python。在包裝重載方法時,SWIG 沒法支持 kwargs,所以沒法使用默認方法。

6.8 保護

SWIG wraps class members that are public following the C++ conventions, i.e., by explicit public declaration or by the use of the using directive. In general, anything specified in a private or protected section will be ignored, although the internal code generator sometimes looks at the contents of the private and protected sections so that it can properly generate code for default constructors and destructors. Directors could also modify the way non-public virtual protected members are treated.

By default, members of a class definition are assumed to be private until you explicitly give a `public:' declaration (This is the same convention used by C++).

SWIG 根據 C++ 約定包裝公有的類成員,即經過顯式公有聲明或使用 using 指令。一般,私有或受保護部分中指定的任何內容都將被忽略,儘管內部代碼生成器有時會查看私有部分和保護部分的內容,以便它能夠正確地爲默認構造函數和析構函數生成代碼。導向器(director)還能夠修改非公有的虛保護成員的處理方式。

默認狀況下,在你明確給出 public 聲明以前,假定類定義的成員是私有的(這與 C++ 使用的約定相同)。

6.9 枚舉與常量

Enumerations and constants are handled differently by the different language modules and are described in detail in the appropriate language chapter. However, many languages map enums and constants in a class definition into constants with the classname as a prefix. For example :

枚舉和常量由不一樣的語言模塊以不一樣方式處理,並在相應的語言章節中詳細描述。可是,許多語言將類定義中的枚舉和常量映射到以類名做爲前綴的常量。例如 :

class Swig {
public:
  enum {ALE, LAGER, PORTER, STOUT};
};

Generates the following set of constants in the target scripting language :

在目標腳本語言中生成如下常量集:

Swig_ALE = Swig::ALE
Swig_LAGER = Swig::LAGER
Swig_PORTER = Swig::PORTER
Swig_STOUT = Swig::STOUT

Members declared as const are wrapped as read-only members and do not create constants.

聲明爲 const 的成員被包裝爲只讀成員,不會建立常量。

6.10 友元

Friend declarations are recognised by SWIG . For example, if you have this code:

SWIG 識別友元聲明。例如,若是你有以下代碼:

class Foo {
public:
  ...
  friend void blah(Foo *f);
  ...
};

then the friend declaration does result in a wrapper code equivalent to one generated for the following declaration

那麼 friend 聲明確實會產生一個等效於如下聲明生成的包裝代碼

class Foo {
public:
    ...
};

void blah(Foo *f);

A friend declaration, as in C++, is understood to be in the same scope where the class is declared, hence, you can have

在 C++ 中,友元聲明被理解爲與聲明類的做用域相同,所以,你有

%ignore bar::blah(Foo *f);

namespace bar {

  class Foo {
  public:
    ...
    friend void blah(Foo *f);
    ...
  };
}

and a wrapper for the method 'blah' will not be generated.

而且 blah 方法的包裝器將不會生成。

6.11 引用與指針

C++ references are supported, but SWIG transforms them back into pointers. For example, a declaration like this:

支持 C++ 引用,但 SWIG 將其轉換回指針。例如,這樣的聲明:

class Foo {
public:
  double bar(double &a);
}

has a low-level accessor

有一個低級訪問器函數

double Foo_bar(Foo *obj, double *a) {
  obj->bar(*a);
}

As a special case, most language modules pass const references to primitive datatypes (int, short, float, etc.) by value instead of pointers. For example, if you have a function like this,

做爲一種特殊狀況,大多數語言模塊都經過值而不是指針傳遞對原始數據類型(intshortfloat 等)的常引用。例如,若是你具備這樣的函數,

void foo(const int &x);

it is called from a script as follows:

在腳本中這樣調用:

foo(3)              # Notice pass by value

Functions that return a reference are remapped to return a pointer instead. For example:

返回引用的函數將從新映射以返回指針。例如:

class Bar {
public:
  Foo &spam();
};

Generates an accessor like this:

建立以下訪問器函數:

Foo *Bar_spam(Bar *obj) {
  Foo &result = obj->spam();
  return &result;
}

However, functions that return const references to primitive datatypes (int, short, etc.) normally return the result as a value rather than a pointer. For example, a function like this,

可是,返回原始數據類型(intshort 等)常引用的函數一般將結果做爲值而不是指針返回。例如,像這樣的函數

const int &bar();

will return integers such as 37 or 42 in the target scripting language rather than a pointer to an integer.

Don't return references to objects allocated as local variables on the stack. SWIG doesn't make a copy of the objects so this will probably cause your program to crash.

Note: The special treatment for references to primitive datatypes is necessary to provide more seamless integration with more advanced C++ wrapping applications---especially related to templates and the STL. This was first added in SWIG-1.3.12.

將在目標腳本語言中返回諸如 37 或 42 之類的整數,而不是指向整數的指針。

不要返回對在堆棧上分配爲局部變量的對象的引用。SWIG 不會複製對象,所以可能會致使程序崩潰。

注意:必須提供對原始數據類型的引用的特殊處理,以提供與更高級的 C++ 包裝應用程序(尤爲是與模板和 STL 有關)的無縫集成。這是在 SWIG-1.3.12 中首次添加的。

6.12 傳值與返回值

Occasionally, a C++ program will pass and return class objects by value. For example, a function like this might appear:

有時,C++ 程序會傳值和返回類對象。例如,可能會出現以下函數:

Vector cross_product(Vector a, Vector b);

If no information is supplied about Vector, SWIG creates a wrapper function similar to the following:

若是未提供有關 Vector 的信息,SWIG 將建立相似於如下內容的包裝器函數:

Vector *wrap_cross_product(Vector *a, Vector *b) {
  Vector x = *a;
  Vector y = *b;
  Vector r = cross_product(x, y);
  return new Vector(r);
}

In order for the wrapper code to compile, Vector must define a copy constructor and a default constructor.

If Vector is defined as a class in the interface, but it does not support a default constructor, SWIG changes the wrapper code by encapsulating the arguments inside a special C++ template wrapper class, through a process called the "Fulton Transform". This produces a wrapper that looks like this:

爲了編譯包裝代碼,Vector 必須定義一個拷貝構造函數和一個默認構造函數。

若是 Vector 在接口中定義爲類,但不支持默認構造函數,則 SWIG 經過稱爲「Fulton Transform」的過程將參數封裝在特殊的 C++ 模板包裝器類中來更改包裝器代碼。這將產生一個以下所示的包裝器:

Vector cross_product(Vector *a, Vector *b) {
  SWIGValueWrapper<Vector> x = *a;
  SWIGValueWrapper<Vector> y = *b;
  SWIGValueWrapper<Vector> r = cross_product(x, y);
  return new Vector(r);
}

This transformation is a little sneaky, but it provides support for pass-by-value even when a class does not provide a default constructor and it makes it possible to properly support a number of SWIG's customization options. The definition of SwigValueWrapper can be found by reading the SWIG wrapper code. This class is really nothing more than a thin wrapper around a pointer.

Although SWIG usually detects the classes to which the Fulton Transform should be applied, in some situations it's necessary to override it. That's done with %feature("valuewrapper") to ensure it is used and %feature("novaluewrapper") to ensure it is not used:

這種轉換有點偷偷摸摸,可是即便類沒有提供默認的構造函數,它也提供了對傳值的支持,而且能夠正確支持許多 SWIG 的自定義選項。經過閱讀 SWIG 包裝器代碼,能夠找到 SwigValueWrapper 的定義。此類實際上僅是指針周圍的輕度包裝器。

儘管 SWIG 一般會檢測應該應用「Fulton Transform」變換的類,但在某些狀況下有必要重寫它。這能夠經過 %feature("valuewrapper") 開啓使用,以及 %feature("novaluewrapper") 關閉使用:

%feature("novaluewrapper") A;
class A;

%feature("valuewrapper") B;
struct B {
    B();
    // ....
};

It is well worth considering turning this feature on for classes that do have a default constructor. It will remove a redundant constructor call at the point of the variable declaration in the wrapper, so will generate notably better performance for large objects or for classes with expensive construction. Alternatively consider returning a reference or a pointer.

Note: this transformation has no effect on typemaps or any other part of SWIG ---it should be transparent except that you may see this code when reading the SWIG output file.

Note: This template transformation is new in SWIG-1.3.11 and may be refined in future SWIG releases. In practice, it is only absolutely necessary to do this for classes that don't define a default constructor.

Note: The use of this template only occurs when objects are passed or returned by value. It is not used for C++ pointers or references.

考慮爲確實具備默認構造函數的類啓用此功能。它將在包裝器中變量聲明的位置刪除多餘的構造函數調用,所以對於大型對象或構形成本高的類將產生明顯更好的性能。或者,考慮返回引用或指針。

注意:此轉換對類型映射或 SWIG 的任何其餘部分沒有影響——它應該是透明的,除了在讀取 SWIG 輸出文件時可能會看到此代碼。

注意:此模板轉換是 SWIG-1.3.11 中的新增功能,可能會在之後的 SWIG 版本中進行完善。實際上,對於沒有定義默認構造函數的類,這樣作絕對是必要的。

注意:僅當對象經過傳值或返回值時才使用此模板。它不用於 C++ 指針或引用。

6.13 繼承

SWIG supports C++ inheritance of classes and allows both single and multiple inheritance, as limited or allowed by the target language. The SWIG type-checker knows about the relationship between base and derived classes and allows pointers to any object of a derived class to be used in functions of a base class. The type-checker properly casts pointer values and is safe to use with multiple inheritance.

SWIG treats private or protected inheritance as close to the C++ spirit, and target language capabilities, as possible. In most cases, this means that SWIG will parse the non-public inheritance declarations, but that will have no effect in the generated code, besides the implicit policies derived for constructors and destructors.

The following example shows how SWIG handles inheritance. For clarity, the full C++ code has been omitted.

SWIG 支持 C++ 類的繼承,並容許單繼承和多繼承,這取決於目標語言的限制。SWIG 類型檢查器瞭解基類和派生類之間的關係,並容許在接受基類的函數中使用指向任何派生類對象的指針。類型檢查器能夠正確地轉換指針值,而且能夠安全地用於多繼承。

SWIG 儘量將私有或保護繼承與 C++ 精神和目標語言功能聯繫起來。在大多數狀況下,這意味着 SWIG 將解析非公有繼承聲明,可是除了爲構造函數和析構函數派生的隱式策略以外,這對生成的代碼無效。

下面的示例顯示 SWIG 如何處理繼承。爲了清楚起見,完整的 C++ 代碼已被省略。

// shapes.i
%module shapes
%{
#include "shapes.h"
%}

class Shape {
public:
  double x, y;
  virtual double area() = 0;
  virtual double perimeter() = 0;
  void    set_location(double x, double y);
};
class Circle : public Shape {
public:
  Circle(double radius);
  ~Circle();
  double area();
  double perimeter();
};
class Square : public Shape {
public:
  Square(double size);
  ~Square();
  double area();
  double perimeter();
}

When wrapped into Python, we can perform the following operations (shown using the low level Python accessors):

當包裝到 Python 中時,咱們能夠執行如下操做(使用低級 Python 訪問器):

$ python
>>> import shapes
>>> circle = shapes.new_Circle(7)
>>> square = shapes.new_Square(10)
>>> print shapes.Circle_area(circle)
153.93804004599999757
>>> print shapes.Shape_area(circle)
153.93804004599999757
>>> print shapes.Shape_area(square)
100.00000000000000000
>>> shapes.Shape_set_location(square, 2, -3)
>>> print shapes.Shape_perimeter(square)
40.00000000000000000
>>>

In this example, Circle and Square objects have been created. Member functions can be invoked on each object by making calls to Circle_area, Square_area, and so on. However, the same results can be accomplished by simply using the Shape_areafunction on either object.

One important point concerning inheritance is that the low-level accessor functions are only generated for classes in which they are actually declared. For instance, in the above example, the methodset_location() is only accessible as Shape_set_location() and not as Circle_set_location() or Square_set_location(). Of course, the Shape_set_location() function will accept any kind of object derived from Shape. Similarly, accessor functions for the attributes x and y are generated as Shape_x_get(), Shape_x_set(), Shape_y_get(), and Shape_y_set(). Functions such as Circle_x_get() are not available--instead you should useShape_x_get().

Note that there is a one to one correlation between the low-level accessor functions and the proxy methods and therefore there is also a one to one correlation between the C++ class methods and the generated proxy class methods.

Note: For the best results, SWIG requires all base classes to be defined in an interface. Otherwise, you may get a warning message like this:

在此示例中,已經建立了 CircleSquare 對象。能夠經過調用 Circle_areaSquare_area 等在每一個對象上調用成員函數。可是,只需在任一對象上使用 Shape_area 函數,便可得到相同的結果。

關於繼承的重要一點是,低級訪問器函數僅針對實際聲明瞭它們的類生成。例如,在以上示例中,只能以 Shape_set_location() 而不是 Circle_set_location()Square_set_location() 訪問 set_location() 方法。固然,Shape_set_location() 函數將接受任何從 Shape 派生的對象。相似地,屬性 xy 的訪問器函數生成爲 Shape_x_get()Shape_x_set()Shape_y_get()Shape_y_set()。諸如 Circle_x_get() 之類的函數不可用——相反,你應使用 Shape_x_get()

請注意,低級訪問器函數與代理方法之間存在一對一的關聯,所以 C++ 類方法與生成的代理類方法之間也具備一一對應的關係。

注意:爲了得到最佳結果,SWIG 要求在接口中定義全部基類。不然,你可能會收到以下警告消息:

example.i:18: Warning 401: Nothing known about base class 'Foo'. Ignored.

If any base class is undefined, SWIG still generates correct type relationships. For instance, a function accepting a Foo * will accept any object derived from Foo regardless of whether or not SWIG actually wrapped the Foo class. If you really don't want to generate wrappers for the base class, but you want to silence the warning, you might consider using the %import directive to include the file that defines Foo. %import simply gathers type information, but doesn't generate wrappers. Alternatively, you could just define Foo as an empty class in the SWIG interface or usewarning suppression.

Note: typedef-names can be used as base classes. For example:

若是未定義任何基類,則 SWIG 仍會生成正確的類型關係。例如,一個接受 Foo * 的函數將接受任何從 Foo 派生的對象,而無論 SWIG 是否實際包裝了 Foo 類。若是你確實不想爲基類生成包裝器,可是想要使警告靜音,則能夠考慮使用 %import 指令包含定義 Foo 的文件。%import 僅收集類型信息,但不生成包裝器。另外,你能夠在 SWIG 接口中將 Foo 定義爲空類,或使用警告抑制

注意typedef-names 能夠用做基類。例如:

class Foo {
...
};

typedef Foo FooObj;
class Bar : public FooObj {     // Ok.  Base class is Foo
...
};

Similarly, typedef allows unnamed structures to be used as base classes. For example:

相似的,typedef 容許未命名的結構體做爲基類使用。例如:

typedef struct {
  ...
} Foo;

class Bar : public Foo {    // Ok.
...
};

Compatibility Note: Starting in version 1.3.7, SWIG only generates low-level accessor wrappers for the declarations that are actually defined in each class. This differs from SWIG 1.1 which used to inherit all of the declarations defined in base classes and regenerate specialized accessor functions such asCircle_x_get(), Square_x_get(), Circle_set_location(), and Square_set_location(). This behavior resulted in huge amounts of replicated code for large class hierarchies and made it awkward to build applications spread across multiple modules (since accessor functions are duplicated in every single module). It is also unnecessary to have such wrappers when advanced features like proxy classes are used.

Note: Further optimizations are enabled when using the -fvirtual option, which avoids the regenerating of wrapper functions for virtual members that are already defined in a base class.

注意兼容性:從版本 1.3.7 開始,SWIG 僅爲每一個類中實際定義的聲明生成低級訪問器函數的包裝器。這與 SWIG 1.1 不一樣,SWIG 1.1 用來繼承基類中定義的全部聲明,並從新生成專用的訪問器函數,例如 Circle_x_get()Square_x_get()Circle_set_location()Square_set_location()。此行爲致使爲大型類層次結構的大量重複代碼,並使構建分佈在多個模塊中的應用程序變得笨拙(由於訪問器功能在每一個模塊中都有重複)。當使用諸如代理類之類的高級功能時,也沒必要具備此類包裝器。

注意:當使用 -fvirtual 選項時,會啓用進一步的優化,這避免了爲已經在基類中定義的虛成員從新生成包裝函數。

6.14 關於多繼承、指針與類型檢查的討論

When a target scripting language refers to a C++ object, it normally uses a tagged pointer object that contains both the value of the pointer and a type string. For example, in Tcl, a C++ pointer might be encoded as a string like this:

當目標腳本語言引用 C++ 對象時,它一般使用帶有標記的指針對象,該對象既包含指針的值,又包含類型字符串。例如,在 Tcl 中,C++ 指針可能被編碼爲這樣的字符串:

_808fea88_p_Circle

A somewhat common question is whether or not the type-tag could be safely removed from the pointer. For instance, to get better performance, could you strip all type tags and just use simple integers instead?

In general, the answer to this question is no. In the wrappers, all pointers are converted into a common data representation in the target language. Typically this is the equivalent of casting a pointer to void *. This means that any C++ type information associated with the pointer is lost in the conversion.

The problem with losing type information is that it is needed to properly support many advanced C++ features--especially multiple inheritance. For example, suppose you had code like this:

一個常見的問題是是否能夠安全地從指針中刪除類型標記。例如,爲了得到更好的性能,是否能夠剝離全部類型標記,而僅使用簡單的整數?

通常來講,這個問題的答案是否認的。在包裝器中,全部指針都轉換爲目標語言中的通用數據表示形式。一般,這等效於將指針轉換爲 void *。這意味着與指針關聯的全部 C++ 類型信息都將在轉換中丟失。

丟失類型信息是個問題,因爲須要正確支持許多高級 C++ 功能,尤爲是多繼承。例如,假設你有以下代碼:

class A {
public:
  int x;
};

class B {
public:
  int y;
};

class C : public A, public B {
};

int A_function(A *a) {
  return a->x;
}

int B_function(B *b) {
  return b->y;
}

Now, consider the following code that uses void *.

如今,考慮使用 void * 的代碼:

C *c = new C();
void *p = (void *) c;
...
int x = A_function((A *) p);
int y = B_function((B *) p);

In this code, both A_function() and B_function() may legally accept an object of type C * (via inheritance). However, one of the functions will always return the wrong result when used as shown. The reason for this is that even though p points to an object of type C, the casting operation doesn't work like you would expect. Internally, this has to do with the data representation of C. With multiple inheritance, the data from each base class is stacked together. For example:

在這段代碼中,A_function()B_function() 均可以合法地接受類型爲 C * 的對象(經過繼承)。可是,如圖所示使用其中一個函數時,老是會返回錯誤的結果。緣由是即便 p 指向類型爲 C 的對象,強制轉換操做也不像你指望的那樣工做。在內部,這與 C 的數據表示有關。經過多繼承,來自每一個基類的數據將堆疊在一塊兒。例如:

------------    <--- (C *), (A *)
            |     A      |
            |------------|   <--- (B *)
            |     B      |
             ------------

Because of this stacking, a pointer of type C * may change value when it is converted to a A * or B *. However, this adjustment does not occur if you are converting from a void *.

The use of type tags marks all pointers with the real type of the underlying object. This extra information is then used by SWIG generated wrappers to correctly cast pointer values under inheritance (avoiding the above problem).

Some of the language modules are able to solve the problem by storing multiple instances of the pointer, for example, A *, in the A proxy class as well as C * in the C proxy class. The correct cast can then be made by choosing the correct void * pointer to use and is guaranteed to work as the cast to a void pointer and back to the same type does not lose any type information:

因爲這種堆疊,類型爲 C * 的指針在轉換爲 A *B * 時可能會改變值。可是,若是從 void * 轉換,則不會進行這種調整。

類型標記的使用將全部指針標記爲底層對象的真實類型。而後,SWIG 生成的包裝器將使用這些額外的信息來在繼承下正確地轉換指針值(避免上述問題)。

一些語言模塊能夠經過在 A 代理類中存儲指針的多個實例(例如,A *)以及在 C 代理類中存儲 C,來解決該問題。而後能夠經過選擇要使用的正確 void * 指針來進行正確的轉換,並保證能夠正常工做,由於轉換爲 void 指針並返回相同類型的轉換不會丟失任何類型信息:

C *c = new C();
void *p = (void *) c;
void *pA = (void *) c;
void *pB = (void *) c;
...
int x = A_function((A *) pA);
int y = B_function((B *) pB);

In practice, the pointer is held as an integral number in the target language proxy class.

實際上,指針在目標語言代理類中被保存爲整數。

6.15 包裝重載函數和方法

In many language modules, SWIG provides partial support for overloaded functions, methods, and constructors. For example, if you supply SWIG with overloaded functions like this:

在許多語言模塊中,SWIG 爲重載的函數、方法和構造函數提供部分支持。例如,若是爲 SWIG 提供像這樣的重載函數:

void foo(int x) {
  printf("x is %d\n", x);
}
void foo(char *x) {
  printf("x is '%s'\n", x);
}

The function is used in a completely natural way. For example:

函數能夠用徹底天然地方式使用。例如:

>>> foo(3)
x is 3
>>> foo("hello")
x is 'hello'
>>>

Overloading works in a similar manner for methods and constructors. For example if you have this code,

方法和構造函數重載的工做方式類似。例如,若是你有以下代碼,

class Foo {
public:
  Foo();
  Foo(const Foo &);   // Copy constructor
  void bar(int x);
  void bar(char *s, int y);
};

it might be used like this

看起來會像這樣

>>> f = Foo()          # Create a Foo
>>> f.bar(3)
>>> g = Foo(f)         # Copy Foo
>>> f.bar("hello", 2)

6.15.1 調度函數生成

The implementation of overloaded functions and methods is somewhat complicated due to the dynamic nature of scripting languages. Unlike C++, which binds overloaded methods at compile time, SWIG must determine the proper function as a runtime check for scripting language targets. This check is further complicated by the typeless nature of certain scripting languages. For instance, in Tcl, all types are simply strings. Therefore, if you have two overloaded functions like this,

因爲腳本語言的動態特性,重載函數和方法的實現有些複雜。與 C++ 會在編譯時綁定重載的方法不一樣,對於目標腳本語言,SWIG 必須以運行時檢查肯定適當的函數。某些腳本語言是無類型的,會使此檢查更加複雜。例如,在 Tcl 中,全部類型都只是字符串。所以,若是你有兩個這樣的重載函數,

void foo(char *x);
void foo(int x);

the order in which the arguments are checked plays a rather critical role.

For statically typed languages, SWIG uses the language's method overloading mechanism. To implement overloading for the scripting languages, SWIG generates a dispatch function that checks the number of passed arguments and their types. To create this function, SWIG first examines all of the overloaded methods and ranks them according to the following rules:

  1. Number of required arguments. Methods are sorted by increasing number of required arguments.
  2. Argument type precedence. All C++ datatypes are assigned a numeric type precedence value (which is determined by the language module).

檢查參數的順序起着相當重要的做用。

對於靜態類型的語言,SWIG 使用該語言的方法重載機制。爲了實現腳本語言的重載,SWIG 生成一個調度函數,該函數檢查傳遞來的參數的數量及其類型。爲了建立此函數,SWIG 首先檢查全部重載方法,而後根據如下規則對它們進行排序:

  1. 所需參數的數量。方法按所需參數數量進行升序排序。
  2. 參數類型優先級。全部 C++ 數據類型均分配有數字類型的優先級值(由語言模塊肯定)。
Type              Precedence
----------------  ----------
TYPE *            0     (High)
void *            20
Integers          40
Floating point    60
char              80
Strings           100   (Low)

Using these precedence values, overloaded methods with the same number of required arguments are sorted in increased order of precedence values.

This may sound very confusing, but an example will help. Consider the following collection of overloaded methods:

使用這些優先級值,具備相同數量必需參數的重載方法將按優先級值進行升序排序。

這聽起來可能很使人困惑,可是一個示例會有所幫助。請考慮如下重載方法的集合:

void foo(double);
void foo(int);
void foo(Bar *);
void foo();
void foo(int x, int y, int z, int w);
void foo(int x, int y, int z = 3);
void foo(double x, double y);
void foo(double x, Bar *z);

The first rule simply ranks the functions by required argument count. This would produce the following list:

第一條規則只是根據所需的參數個數對函數進行排序。這將產生如下列表:

rank
-----
[0]   foo()
[1]   foo(double);
[2]   foo(int);
[3]   foo(Bar *);
[4]   foo(int x, int y, int z = 3);
[5]   foo(double x, double y)
[6]   foo(double x, Bar *z)
[7]   foo(int x, int y, int z, int w);

The second rule, simply refines the ranking by looking at argument type precedence values.

第二條規則只是經過查看參數類型優先級值來簡化排序。

rank
-----
[0]   foo()
[1]   foo(Bar *);
[2]   foo(int);
[3]   foo(double);
[4]   foo(int x, int y, int z = 3);
[5]   foo(double x, Bar *z)
[6]   foo(double x, double y)
[7]   foo(int x, int y, int z, int w);

Finally, to generate the dispatch function, the arguments passed to an overloaded method are simply checked in the same order as they appear in this ranking.

If you're still confused, don't worry about it--- SWIG is probably doing the right thing.

最後,要生成調度函數,只需按照該排序中的順序檢查傳遞給重載方法的參數。

若是你仍然感到困惑,請不要擔憂——SWIG 不會出錯。

6.15.2 重載中的歧義

Regrettably, SWIG is not able to support every possible use of valid C++ overloading. Consider the following example:

遺憾的是,SWIG 沒法支持全部有效 C++ 重載的可能使用情形。考慮如下示例:

void foo(int x);
void foo(long x);

In C++, this is perfectly legal. However, in a scripting language, there is generally only one kind of integer object. Therefore, which one of these functions do you pick? Clearly, there is no way to truly make a distinction just by looking at the value of the integer itself (int and long may even be the same precision). Therefore, when SWIG encounters this situation, it may generate a warning message like this for scripting languages:

在 C++ 中,這是徹底合法的。可是,在腳本語言中,一般只有一種整數對象。所以,你選擇的是哪個函數?顯然,僅經過查看整數自己的值沒法真正作出區分(intlong 甚至可能具備相同的精度)。所以,當 SWIG 遇到這種狀況時,它可能會針對腳本語言生成以下警告消息:

example.i:4: Warning 509: Overloaded method foo(long) effectively ignored,
example.i:3: Warning 509: as it is shadowed by foo(int).

or for statically typed languages like Java:

或者,對於 Java 這樣的靜態類型語言:

example.i:4: Warning 516: Overloaded method foo(long) ignored,
example.i:3: Warning 516: using foo(int) instead.
at example.i:3 used.

This means that the second overloaded function will be inaccessible from a scripting interface or the method won't be wrapped at all. This is done as SWIG does not know how to disambiguate it from an earlier method.

Ambiguity problems are known to arise in the following situations:

  • Integer conversions. Datatypes such as int, long, and shortcannot be disambiguated in some languages. Shown above.
  • Floating point conversion. float and double can not be disambiguated in some languages.
  • Pointers and references. For example, Foo * and Foo &.
  • Pointers and arrays. For example, Foo * and Foo [4].
  • Pointers and instances. For example, Foo and Foo *. Note: SWIG converts all instances to pointers.
  • Qualifiers. For example, const Foo * and Foo *.
  • Default vs. non default arguments. For example, foo(int a, int b) and foo(int a, int b = 3).

When an ambiguity arises, methods are checked in the same order as they appear in the interface file. Therefore, earlier methods will shadow methods that appear later.

When wrapping an overloaded function, there is a chance that you will get a warning message like this:

這意味着第二個重載函數將沒法從腳本接口訪問,或者該方法將不會被包裝。之因此這樣作是由於 SWIG 不知道如何將其與先出現的方法進行區分。

已知在如下狀況下會出現歧義問題:

  • 整數轉換。在某些語言中,諸如 intlongshort 之類的數據類型沒法消除歧義。如上所示。
  • 浮點轉換。在某些語言中,floatdouble 沒法消除歧義。
  • 指針和引用。例如,Foo *Foo&
  • 指針和數組。例如,Foo *Foo [4]
  • 指針和實例。例如,FooFoo *。注意: SWIG 將全部實例轉換爲指針。
  • 限定詞。例如,const Foo *Foo *
  • 默認與非默認參數。例如,foo(int a, int b)foo(int a, int b = 3)

當出現歧義時,將按照接口文件中出現的順序檢查方法。所以,先出現的方法將覆蓋後出現的方法。

當包裝一個重載的函數時,你可能會收到以下警告消息:

example.i:3: Warning 467: Overloaded foo(int) not supported (incomplete type checking rule -
no precedence level in typecheck typemap for 'int').

This error means that the target language module supports overloading, but for some reason there is no type-checking rule that can be used to generate a working dispatch function. The resulting behavior is then undefined. You should report this as a bug to the SWIG bug tracking database if this is due to one of the typemaps supplied with SWIG .

If you get an error message such as the following,

該錯誤意味着目標語言模塊支持重載,可是因爲某種緣由,沒有類型檢查規則可用於生成有效的調度函數。最終的行爲是不肯定的。若是這是由 SWIG 附帶的類型映射帶來的,則應將此錯誤報告給 SWIG 錯誤跟蹤數據庫

若是你收到如下錯誤消息,

foo.i:6. Overloaded declaration ignored.  Spam::foo(double )
foo.i:5. Previous declaration is Spam::foo(int )
foo.i:7. Overloaded declaration ignored.  Spam::foo(Bar *, Spam *, int )
foo.i:5. Previous declaration is Spam::foo(int )

it means that the target language module has not yet implemented support for overloaded functions and methods. The only way to fix the problem is to read the next section.

這意味着目標語言模塊還沒有實現對重載函數和方法的支持。解決該問題的惟一方法是閱讀下一節。

6.15.3 消歧義與重命名

If an ambiguity in overload resolution occurs or if a module doesn't allow overloading, there are a few strategies for dealing with the problem. First, you can tell SWIG to ignore one of the methods. This is easy---simply use the %ignore directive. For example:

若是在重載上出現歧義,或者模塊不容許重載,則有一些策略能夠解決該問題。首先,你能夠告訴 SWIG 忽略其中一種方法。這很容易——只需使用 %ignore 指令便可。例如:

%ignore foo(long);

void foo(int);
void foo(long);       // Ignored.  Oh well.

The other alternative is to rename one of the methods. This can be done using %rename. For example:

另外一種選擇是重命名其中一種方法。這可使用 %rename 完成。例如:

%rename("foo_short") foo(short);
%rename(foo_long) foo(long);

void foo(int);
void foo(short);      // Accessed as foo_short()
void foo(long);       // Accessed as foo_long()

Note that the quotes around the new name are optional, however, should the new name be a C/C++ keyword they would be essential in order to avoid a parsing error. The %ignore and %renamedirectives are both rather powerful in their ability to match declarations. When used in their simple form, they apply to both global functions and methods. For example:

請注意,新名稱周圍的引號是可選的,可是,爲避免解析錯誤,新名稱不能是 C/C++ 關鍵字。%ignore%rename 指令在匹配聲明方面都很是強大。當以簡單形式使用時,它們適用於全局函數和方法。例如:

/* Forward renaming declarations */
%rename(foo_i) foo(int);
%rename(foo_d) foo(double);
...
void foo(int);           // Becomes 'foo_i'
void foo(char *c);       // Stays 'foo' (not renamed)

class Spam {
public:
  void foo(int);      // Becomes 'foo_i'
  void foo(double);   // Becomes 'foo_d'
  ...
};

If you only want the renaming to apply to a certain scope, the C++ scope resolution operator (::) can be used. For example:

若是隻想將重命名應用於某個範圍,則可使用 C++ 範圍解析運算符(::)。例如:

%rename(foo_i) ::foo(int);      // Only rename foo(int) in the global scope.
                                // (will not rename class members)

%rename(foo_i) Spam::foo(int);  // Only rename foo(int) in class Spam

When a renaming operator is applied to a class as in Spam::foo(int), it is applied to that class and all derived classes. This can be used to apply a consistent renaming across an entire class hierarchy with only a few declarations. For example:

當將重命名運算符應用於 Spam::foo(int) 中的類時,它將應用於該類和全部派生類。這樣一來,僅使用幾個聲明就能夠在整個類層次結構中應用一致的重命名。例如:

%rename(foo_i) Spam::foo(int);
%rename(foo_d) Spam::foo(double);

class Spam {
public:
  virtual void foo(int);      // Renamed to foo_i
  virtual void foo(double);   // Renamed to foo_d
  ...
};

class Bar : public Spam {
public:
  virtual void foo(int);      // Renamed to foo_i
  virtual void foo(double);   // Renamed to foo_d
  ...
};

class Grok : public Bar {
public:
  virtual void foo(int);      // Renamed to foo_i
  virtual void foo(double);   // Renamed to foo_d
  ...
};

It is also possible to include %rename specifications in the class definition itself. For example:

也能夠在類定義自己中包含 %rename 規範。例如:

class Spam {
  %rename(foo_i) foo(int);
  %rename(foo_d) foo(double);
public:
  virtual void foo(int);      // Renamed to foo_i
  virtual void foo(double);   // Renamed to foo_d
  ...
};

class Bar : public Spam {
public:
  virtual void foo(int);      // Renamed to foo_i
  virtual void foo(double);   // Renamed to foo_d
...
};

In this case, the %rename directives still get applied across the entire inheritance hierarchy, but it's no longer necessary to explicitly specify the class prefix Spam::.

A special form of %rename can be used to apply a renaming just to class members (of all classes):

在這種狀況下,%rename 指令仍將應用於整個繼承層次結構,可是再也不須要顯式指定類前綴 Spam::

%rename 的特殊形式可用於將重命名僅應用於類成員(全部類的):

%rename(foo_i) *::foo(int);   // Only rename foo(int) if it appears in a class.

Note: the *:: syntax is non-standard C++, but the '*' is meant to be a wildcard that matches any class name (we couldn't think of a better alternative so if you have a better idea, send email to the swig-devel mailing list.

Although this discussion has primarily focused on %rename all of the same rules also apply to %ignore. For example:

注意*:: 語法是非標準的 C++ ,可是 * 是一個與任何類名匹配的通配符(咱們想不出更好的替代方法,所以,若是你有更好的主意,請發送經過電子郵件發送到 swig-devel 郵件列表

儘管此討論主要集中在 %rename 上,全部相同的規則也適用於 %ignore。例如:

%ignore foo(double);          // Ignore all foo(double)
%ignore Spam::foo;            // Ignore foo in class Spam
%ignore Spam::foo(double);    // Ignore foo(double) in class Spam
%ignore *::foo(double);       // Ignore foo(double) in all classes

When applied to a base class, %ignore forces all definitions in derived classes to disappear. For example, %ignore Spam::foo(double) will eliminate foo(double) in Spam and all classes derived from Spam.

Notes on %rename and %ignore:

  • Since, the %rename declaration is used to declare a renaming in advance, it can be placed at the start of an interface file. This makes it possible to apply a consistent name resolution without having to modify header files. For example:

當應用於基類時,%ignore 會強制派生類中的全部定義消失。例如,%ignore Spam::foo(double) 將消除 Spam 和全部 Spam 派生類中的 foo(double)

有關 %rename%ignore 的說明:

  • 因爲 %rename 聲明用於預先聲明重命名,所以能夠將其放在接口文件的開頭。這樣就能夠應用一致的名稱解析,而沒必要修改頭文件。例如:
%module foo

/* Rename these overloaded functions */
%rename(foo_i) foo(int);
%rename(foo_d) foo(double);

%include "header.h"
  • The scope qualifier (::) can also be used on simple names. For example:
  • 範圍限定符(::)也能夠用於簡單名稱。例如:
%rename(bar) ::foo;       // Rename foo to bar in global scope only
%rename(bar) Spam::foo;   // Rename foo to bar in class Spam only
%rename(bar) *::foo;      // Rename foo in classes only
  • Name matching tries to find the most specific match that is defined. A qualified name such as Spam::foo always has higher precedence than an unqualified name foo. Spam::foohas higher precedence than *::foo and *::foo has higher precedence than foo. A parameterized name has higher precedence than an unparameterized name within the same scope level. However, an unparameterized name with a scope qualifier has higher precedence than a parameterized name in global scope (e.g., a renaming of Spam::foo takes precedence over a renaming of foo(int)).
  • The order in which %rename directives are defined does not matter as long as they appear before the declarations to be renamed. Thus, there is no difference between saying:
  • 名稱匹配嘗試查找已定義的最具體匹配。諸如 Spam::foo 之類的限定名稱老是比非限定名稱 foo 具備更高的優先級。Spam::foo 的優先級高於 *::foo,而 *::foo 的優先級高於 foo。在相同做用域級別中,參數化名稱的優先級高於未參數化名稱的優先級。可是,具備範圍限定符的未參數化名稱的優先級高於全局範圍內的參數化名稱的優先級(例如,Spam::foo 的重命名優先於 foo(int) 的重命名)。
  • 定義 %rename 指令的順序無所謂,只要它們出如今要重命名的聲明以前便可。所以,說下面兩個沒有區別:
%rename(bar) foo;
%rename(foo_i) Spam::foo(int);
%rename(Foo) Spam::foo;

and this

%rename(Foo) Spam::foo;
%rename(bar) foo;
%rename(foo_i) Spam::foo(int);

(the declarations are not stored in a linked list and order has no importance). Of course, a repeated %rename directive will change the setting for a previous %rename directive if exactly the same name, scope, and parameters are supplied.

  • For multiple inheritance where renaming rules are defined for multiple base classes, the first renaming rule found on a depth-first traversal of the class hierarchy is used.

  • The name matching rules strictly follow member qualification rules. For example, if you have a class like this:

(聲明不存儲在連接列表中,順序不重要)。固然,若是提供的名稱、做用域和參數徹底相同,重複的 %rename 指令將更改先前的 %rename 指令的設置。

  • 對於多繼承,若是多個基類定義了重命名規則,使用類層次結構的深度優先遍歷中找到的第一個重命名規則。
  • 名稱匹配規則嚴格遵循成員限定規則。例如,若是你有一個像這樣的類:
class Spam {
public:
  ...
  void bar() const;
  ...
};

the declaration

聲明

%rename(name) Spam::bar();

will not apply as there is no unqualified member bar(). The following will apply as the qualifier matches correctly:

將不適用,由於沒有不合法的成員 bar()。當限定詞正確匹配時,將適用如下條件:

%rename(name) Spam::bar() const;

An often overlooked C++ feature is that classes can define two different overloaded members that differ only in their qualifiers, like this:

一個常常被忽視的 C++ 功能是,類能夠定義兩個不一樣的重載成員函數,僅僅是它們的限定符不一樣,以下所示:

class Spam {
public:
...
void bar();         // Unqualified member
void bar() const;   // Qualified member
...
};

%rename can then be used to target each of the overloaded methods individually. For example we can give them separate names in the target language:

而後,可使用 %rename 分別針對每一個重載方法。例如,咱們能夠在目標語言中給它們單獨命名:

%rename(name1) Spam::bar();
%rename(name2) Spam::bar() const;

Similarly, if you merely wanted to ignore one of the declarations, use %ignore with the full qualification. For example, the following directive would tell SWIG to ignore the const version of bar() above:

一樣,若是你只想忽略其中一個聲明,請使用帶有完整限定符的 %ignore。例如,如下指令將告訴 SWIG 忽略上述 bar()const 版本:

%ignore Spam::bar() const;   // Ignore bar() const, but leave other bar() alone
  • Currently no resolution is performed in order to match function parameters. This means function parameter types must match exactly. For example, namespace qualifiers and typedefs will not work. The following usage of typedefs demonstrates this:
  • 當前不執行區分以匹配函數參數。這意味着函數參數類型必須徹底匹配。例如,命名空間限定符和 typedef 將不起做用。typedef 的如下用法說明了這一點:
typedef int Integer;

%rename(foo_i) foo(int);

class Spam {
public:
  void foo(Integer);  // Stays 'foo' (not renamed)
};
class Ham {
public:
  void foo(int);      // Renamed to foo_i
};
  • The name matching rules also use default arguments for finer control when wrapping methods that have default arguments. Recall that methods with default arguments are wrapped as if the equivalent overloaded methods had been parsed (Default arguments section). Let's consider the following example class:
  • 當包裝具備默認參數的方法時,名稱匹配規則還使用默認參數來進行更好的控制。回想一下,帶有默認參數的方法被包裝起來,就好像解析了等效的重載方法同樣(默認參數部分)。讓咱們考慮如下示例類:
class Spam {
public:
  ...
  void bar(int i=-1, double d=0.0);
  ...
};

The following %rename will match exactly and apply to all the target language overloaded methods because the declaration with the default arguments exactly matches the wrapped method:

如下 %rename 將徹底匹配並適用於全部目標語言重載方法,由於帶有默認參數的聲明與包裝的方法徹底匹配:

%rename(newbar) Spam::bar(int i=-1, double d=0.0);

The C++ method can then be called from the target language with the new name no matter how many arguments are specified, for example: newbar(2, 2.0), newbar(2) or newbar(). However, if the %rename does not contain the default arguments, it will only apply to the single equivalent target language overloaded method. So if instead we have:

而後,不管指定多少個參數,均可以使用新名稱從目標語言中調用 C++ 方法,例如:newbar(2, 2.0)newbar(2)newbar()。可是,若是 %rename 不包含默認參數,它將僅適用於單個等效的目標語言重載方法。所以,若是相反,咱們有:

%rename(newbar) Spam::bar(int i, double d);

The C++ method must then be called from the target language with the new name newbar(2, 2.0) when both arguments are supplied or with the original name as bar(2) (one argument) or bar() (no arguments). In fact it is possible to use %rename on the equivalent overloaded methods, to rename all the equivalent overloaded methods:

若是同時提供了兩個參數,則必須使用新名稱 newbar(2, 2.0) 從目標語言中調用 C++ 方法,或者將原始名稱稱爲 bar(2)(一個參數)或 bar()(無參數)。實際上,能夠在等效的重載方法上使用 %rename 來重命名全部等效的重載方法:

%rename(bar_2args)   Spam::bar(int i, double d);
%rename(bar_1arg)    Spam::bar(int i);
%rename(bar_default) Spam::bar();

Similarly, the extra overloaded methods can be selectively ignored using %ignore.

Compatibility note: The %rename directive introduced the default argument matching rules in SWIG-1.3.23 at the same time as the changes to wrapping methods with default arguments was introduced.

相似地,額外的重載方法可使用 %ignore 有選擇地忽略。

注意兼容性%rename 指令在 SWIG-1.3.23 中引入了默認參數匹配規則,同時改變了對帶有默認參數的方法的包裝方式。

6.15.4 對重載的評論

Support for overloaded methods was first added in SWIG-1.3.14. The implementation is somewhat unusual when compared to similar tools. For instance, the order in which declarations appear is largely irrelevant in SWIG . Furthermore, SWIG does not rely upon trial execution or exception handling to figure out which method to invoke.

Internally, the overloading mechanism is completely configurable by the target language module. Therefore, the degree of overloading support may vary from language to language. As a general rule, statically typed languages like Java are able to provide more support than dynamically typed languages like Perl, Python, Ruby, and Tcl.

SWIG-1.3.14 首先添加了對重載方法的支持。與相似工具相比,該實現有些不一樣尋常。例如,聲明出現的順序在 SWIG 中基本上可有可無。此外,SWIG 並不依靠試驗執行或異常處理來肯定要調用的方法。

在內部,重載機制能夠由目標語言模塊徹底配置。所以,重載支持的程度可能因語言而異。一般,與 Perl、Python、Ruby 和 Tcl 等動態類型的語言相比,像 Java 這樣的靜態類型語言可以提供更多的支持。

6.16 包裝重載運算符

C++ overloaded operator declarations can be wrapped. For example, consider a class like this:

能夠包裝 C++ 重載運算符聲明。例如,考慮這樣的一個類:

class Complex {
private:
  double rpart, ipart;
public:
  Complex(double r = 0, double i = 0) : rpart(r), ipart(i) { }
  Complex(const Complex &c) : rpart(c.rpart), ipart(c.ipart) { }
  Complex &operator=(const Complex &c) {
    rpart = c.rpart;
    ipart = c.ipart;
    return *this;
  }
  Complex operator+(const Complex &c) const {
    return Complex(rpart+c.rpart, ipart+c.ipart);
  }
  Complex operator-(const Complex &c) const {
    return Complex(rpart-c.rpart, ipart-c.ipart);
  }
  Complex operator*(const Complex &c) const {
    return Complex(rpart*c.rpart - ipart*c.ipart,
                   rpart*c.ipart + c.rpart*ipart);
  }
  Complex operator-() const {
    return Complex(-rpart, -ipart);
  }
  double re() const { return rpart; }
  double im() const { return ipart; }
};

When operator declarations appear, they are handled in exactly the same manner as regular methods. However, the names of these methods are set to strings like "operator +" or "operator -". The problem with these names is that they are illegal identifiers in most scripting languages. For instance, you can't just create a method called "operator +" in Python--there won't be any way to call it.

Some language modules already know how to automatically handle certain operators (mapping them into operators in the target language). However, the underlying implementation of this is really managed in a very general way using the %rename directive. For example, in Python a declaration similar to this is used:

出現運算符聲明時,它們的處理方式與常規方法徹底相同。可是,這些方法的名稱被這樣的設置爲字符串,例如 operator +operator -。這些名稱的問題在於,它們在大多數腳本語言中是非法標識符。例如,你不能在 Python 中建立一個稱爲 operator + 的方法——不會有任何方法能夠調用它。

一些語言模塊已經知道如何自動處理某些運算符(將它們映射爲目標語言的運算符)。可是,實際上使用 %rename 指令以一種很是通用的方式來管理它的底層實現。例如,在 Python 中,使用相似於如下的聲明:

%rename(__add__) Complex::operator+;

This binds the + operator to a method called __add__ (which is conveniently the same name used to implement the Python + operator). Internally, the generated wrapper code for a wrapped operator will look something like this pseudocode:

它將 + 運算符綁定到名爲 __add__ 的方法(方便地與用於實現 Python + 運算符的名稱相同)。在內部,爲包裝的運算符生成的包裝器代碼將相似於如下僞代碼:

_wrap_Complex___add__(args) {
  ... get args ...
  obj->operator+(args);
  ...
}

When used in the target language, it may now be possible to use the overloaded operator normally. For example:

當以目標語言使用時,如今能夠正常使用重載運算符了。例如:

>>> a = Complex(3, 4)
>>> b = Complex(5, 2)
>>> c = a + b           # Invokes __add__ method

It is important to realize that there is nothing magical happening here. The %rename directive really only picks a valid method name. If you wrote this:

重要的是要意識到這裏沒有神奇的事情發生。%rename 指令實際上只選擇一個有效的方法名稱。若是你編寫下列代碼:

%rename(add) operator+;

The resulting scripting interface might work like this:

最終的腳本接口會像這樣:

a = Complex(3, 4)
b = Complex(5, 2)
c = a.add(b)      # Call a.operator+(b)

All of the techniques described to deal with overloaded functions also apply to operators. For example:

以前描述的處理重載函數的全部技術也適用於運算符。例如:

%ignore Complex::operator=;             // Ignore = in class Complex
%ignore *::operator=;                   // Ignore = in all classes
%ignore operator=;                      // Ignore = everywhere.

%rename(__sub__) Complex::operator-;
%rename(__neg__) Complex::operator-();  // Unary -

The last part of this example illustrates how multiple definitions of the operator- method might be handled.

Handling operators in this manner is mostly straightforward. However, there are a few subtle issues to keep in mind:

  • In C++, it is fairly common to define different versions of the operators to account for different types. For example, a class might also include a friend function like this:

該示例的最後一部分說明了如何處理 operator - 方法的多個定義。

以這種方式處理運算符一般很簡單。可是,請記住一些細微的問題:

  • 在 C++ 中,定義不一樣版本的運算符以對應不一樣類型是至關廣泛的。例如,一個類可能還包含一個以下所示的友元函數:
class Complex {
public:
  friend Complex operator+(Complex &, double);
};
Complex operator+(Complex &, double);

SWIG simply ignores all friend declarations. Furthermore, it doesn't know how to associate the associated operator+ with the class (because it's not a member of the class).

It's still possible to make a wrapper for this operator, but you'll have to handle it like a normal function. For example:

SWIG 簡單地忽略全部 friend 聲明。此外,它不知道如何將 operator + 與該類相關聯(由於它不是該類的成員)。

仍然能夠爲該運算符建立包裝器,可是你必須像處理普通函數同樣處理它。例如:

%rename(add_complex_double) operator+(Complex &, double);
  • Certain operators are ignored by default. For instance, newand delete operators are ignored as well as conversion and index operators. A warning such as the one below is shown:
  • 默認狀況下會忽略某些運算符。例如,newdelete 運算符以及轉換和索引運算符都將被忽略。顯示以下警告:
example.i:12: Warning 503: Can't wrap 'operator []' unless renamed to a valid identifier.
  • The index operator, operator[], is particularly difficult to overload due to differences in C++ implementations. Specifically, the get and set operators in other languages typically are separated into two methods such that additional logic can be packed into the operations; C# uses this[type key] { get { ... } set { ... }}, Python uses__getitem__ and __setitem__, etc. In C++ if the return type of operator[] is a reference and the method is const, it is often indicative of the setter, and and the getter is usually a const function return an object by value. In the absence of any hard and fast rules and the fact that there may be multiple index operators, it is up to the user to choose the getter and setter to use by using %rename as shown earlier.

  • The semantics of certain C++ operators may not match those in the target language.

  • 因爲 C++ 實現的差別,索引運算符 operator [] 特別難以重載。具體來講,其餘語言中的 get 和 set 運算符一般分爲兩種方法,以即可以將其餘邏輯打包到這些運算中;C# 使用 this[type key] { get { ... } set { ... }},Python 使用 __getitem____setitem__,等等。在 C++ 中,若是 operator [] 的返回類型是引用而且方法是常量的,它一般表示 setter,而 getter 一般是一個常量函數,經過值返回對象。在沒有任何嚴格的規則且有多個索引運算符的狀況下,用戶能夠經過使用 %rename 來選擇要使用的 getter 和 setter,如先前所示。
  • 某些 C++ 運算符的語義可能與目標語言中的語義不匹配。

6.17 對類的擴展

New methods can be added to a class using the %extend directive. This directive is primarily used in conjunction with proxy classes to add additional functionality to an existing class. For example :

可使用 %extend 指令將新方法添加到類中。該指令主要與代理類結合使用,以向現有類添加其餘功能。例如:

%module vector
%{
#include "vector.h"
%}

class Vector {
public:
  double x, y, z;
  Vector();
  ~Vector();
  ... bunch of C++ methods ...
  %extend {
    char *__str__() {
      static char temp[256];
      sprintf(temp, "[ %g, %g, %g ]", $self->x, $self->y, $self->z);
      return &temp[0];
    }
  }
};

This code adds a __str__ method to our class for producing a string representation of the object. In Python, such a method would allow us to print the value of an object using the printcommand.

這段代碼向咱們的類中添加了一個 __str__ 方法,用於生成對象的字符串表示形式。在 Python 中,這種方法將容許咱們使用 print 命令來打印對象的值。

>>>
>>> v = Vector();
>>> v.x = 3
>>> v.y = 4
>>> v.z = 0
>>> print(v)
[ 3.0, 4.0, 0.0 ]
>>>

The C++ 'this' pointer is often needed to access member variables, methods etc. The $self special variable should be used wherever you could use 'this'. The example above demonstrates this for accessing member variables. Note that the members dereferenced by $self must be public members as the code is ultimately generated into a global function and so will not have any access to non-public members. The implicit 'this' pointer that is present in C++ methods is not present in %extend methods. In order to access anything in the extended class or its base class, an explicit 'this' is required. The following example shows how one could access base class members:

常常須要 C++ 的 this 指針來訪問成員變量、方法等。在任何可使用 this 的地方都應使用 $self 特殊變量。上面的示例演示瞭如何訪問成員變量。請注意,因爲最終將代碼生成到全局函數中,所以被 $self 解引用的成員必須是公有成員,所以將沒法訪問非公有成員。C++ 方法中存在的隱式 this 指針在 %extend 方法中不存在。爲了訪問擴展類或其基類中的任何內容,須要顯式的 this。如下示例顯示瞭如何訪問基類成員:

struct Base {
  virtual void method(int v) {
    ...
  }
  int value;
};
struct Derived : Base {
};
%extend Derived {
  virtual void method(int v) {
    $self->Base::method(v); // akin to this->Base::method(v);
    $self->value = v;       // akin to this->value = v;
    ...
  }
}

The following special variables are expanded if used within a %extend block: $name, $symname, $overname, $decl, $fulldecl, $parentclassname and $parentclasssymname. The Special variables section provides more information each of these special variables.

The %extend directive follows all of the same conventions as its use with C structures. Please refer to the Adding member functions to C structures section for further details.

Compatibility note: The %extend directive is a new name for the %addmethods directive in SWIG 1.1. Since %addmethods could be used to extend a structure with more than just methods, a more suitable directive name has been chosen.

若是在 %extend 塊中使用如下特殊變量,則會對其進行擴展:$name$symname$overname$decl$fulldecl$parentclassname$parentclasssymname特殊變量部分提供了每一個這些特殊變量的更多信息。

%extend 指令遵循與 C 結構體一塊兒使用時同樣的約定。有關更多詳細信息,請參閱向 C 結構體添加成員函數

注意兼容性%extend 指令是 SWIG 1.1 中 %addmethods 指令的新名稱。因爲 %addmethods 能夠用於擴展結構體而不只僅是方法,所以選擇了一個更合適的指令名稱。

6.18 模板

Template type names may appear anywhere a type is expected in an interface file. For example:

模板類型名稱可能會出如今接口文件中任何須要該類型的位置。例如:

void foo(vector<int> *a, int n);
void bar(list<int, 100> *x);

There are some restrictions on the use of non-type arguments. Simple literals are supported, and so are some constant expressions. However, use of '<' and '>' within a constant expressions currently is not supported by SWIG ('<=' and '>=' are though). For example:

使用非類型參數有一些限制。支持簡單文字,某些常量表達式也受支持。可是,SWIG 當前不支持在常量表達式中使用 <>(可是使用 <=> =)。例如:

void bar(list<int, 100> *x);                // OK
void bar(list<int, 2*50> *x);               // OK
void bar(list<int, (2>1 ? 100 : 50)> *x)    // Not supported

The type system is smart enough to figure out clever games you might try to play with typedef. For instance, consider this code:

類型系統足夠聰明,能夠找出你能夠嘗試使用 typedef 玩的把戲。例如,考慮如下代碼:

typedef int Integer;
void foo(vector<int> *x, vector<Integer> *y);

In this case, vector<Integer> is exactly the same type as vector<int>. The wrapper for foo() will accept either variant.

Starting with SWIG-1.3.7, simple C++ template declarations can also be wrapped. SWIG-1.3.12 greatly expands upon the earlier implementation. Before discussing this any further, there are a few things you need to know about template wrapping. First, a bare C++ template does not define any sort of runnable object-code for which SWIG can normally create a wrapper. Therefore, in order to wrap a template, you need to give SWIG information about a particular template instantiation (e.g., vector<int>,array<double>, etc.). Second, an instantiation name such as vector<int> is generally not a valid identifier name in most target languages. Thus, you will need to give the template instantiation a more suitable name such as intvector when creating a wrapper.

To illustrate, consider the following template definition:

在這種狀況下,vector<Integer>vector<int> 的類型徹底相同。foo() 的包裝器將接受任何一個變體。

從 SWIG-1.3.7 開始,還能夠包裝簡單的 C++ 模板聲明。SWIG-1.3.12 在較早的實現上有了很大的擴展。在進一步討論以前,你須要瞭解一些有關模板包裝的知識。首先,裸露的 C++ 模板沒有定義任何類型的可運行目標代碼以供 SWIG 爲其建立包裝器。所以,爲了包裝模板,你須要爲 SWIG 提供有關特定模板實例化的信息(例如,vector<int>array<double> 等)。其次,在大多數目標語言中,諸如 vector<int> 之類的實例化名稱一般不是有效的標識符名稱。所以,在建立包裝器時,須要爲模板實例化指定一個更合適的名稱,例如 intvector

爲了說明,請考慮如下模板定義:

template<class T> class List {
private:
    T *data;
    int nitems;
    int maxitems;
public:
    List(int max) {
      data = new T [max];
      nitems = 0;
      maxitems = max;
    }
    ~List() {
      delete [] data;
    };
    void append(T obj) {
      if (nitems < maxitems) {
        data[nitems++] = obj;
      }
    }
    int length() {
      return nitems;
    }
    T get(int n) {
      return data[n];
    }
};

By itself, this template declaration is useless-- SWIG simply ignores it because it doesn't know how to generate any code until unless a definition of T is provided.

One way to create wrappers for a specific template instantiation is to simply provide an expanded version of the class directly like this:

就其自己而言,此模板聲明是無用的——SWIG 只會忽略它,由於除非提供 T 的定義,不然它不知道如何生成代碼。

爲特定模板實例建立包裝器的一種方法是,直接像下面這樣直接提供類的擴展版本:

%rename(intList) List<int>;       // Rename to a suitable identifier
class List<int> {
private:
    int *data;
    int nitems;
    int maxitems;
public:
    List(int max);
    ~List();
    void append(int obj);
    int length();
    int get(int n);
};

The %rename directive is needed to give the template class an appropriate identifier name in the target language (most languages would not recognize C++ template syntax as a valid class name). The rest of the code is the same as what would appear in a normal class definition.

Since manual expansion of templates gets old in a hurry, the %template directive can be used to create instantiations of a template class. Semantically, %template is simply a shortcut---it expands template code in exactly the same way as shown above. Here are some examples:

須要使用 %rename 指令,在目標語言爲模板類提供適當的標識符名稱(大多數語言沒法將 C++ 模板語法識別爲有效的類名稱)。其他代碼與普通類定義中顯示的代碼相同。

因爲模板的手動擴展已經很老舊了,所以可使用 %template 指令來建立模板類的實例化。從語義上講,%template 只是一種快捷方式——它以與上面所示徹底相同的方式擴展模板代碼。這裏有些例子:

/* Instantiate a few different versions of the template */
%template(intList) List<int>;
%template(doubleList) List<double>;

The argument to %template() is the name of the instantiation in the target language. The name you choose should not conflict with any other declarations in the interface file with one exception---it is okay for the template name to match that of a typedef declaration. For example:

%template() 的參數是目標語言中實例化的名稱。你選擇的名稱不該與接口文件中的任何其餘聲明相沖突,只有一個例外——模板名稱能夠與 typedef 聲明的名稱匹配。例如:

%template(intList) List<int>;
...
typedef List<int> intList;    // OK

SWIG can also generate wrappers for function templates using a similar technique. For example:

SWIG 還可使用相似的技術爲函數模板生成包裝器。例如:

// Function template
template<class T> T max(T a, T b) { return a > b ? a : b; }

// Make some different versions of this function
%template(maxint) max<int>;
%template(maxdouble) max<double>;

In this case, maxint and maxdouble become unique names for specific instantiations of the function.

The number of arguments supplied to %template should match that in the original template definition. Template default arguments are supported. For example:

在這種狀況下,對於函數的特定實例,maxintmaxdouble 成爲惟一的名稱。

提供給 %template 的參數數量應與原始模板定義中的參數數量匹配。支持模板默認參數。例如:

template vector<typename T, int max=100> class vector {
...
};

%template(intvec) vector<int>;           // OK
%template(vec1000) vector<int, 1000>;     // OK

The %template directive should not be used to wrap the same template instantiation more than once in the same scope. This will generate an error. For example:

%template 指令不該在同一做用域中屢次包裝相同的模板實例。這將產生一個錯誤。例如:

%template(intList) List<int>;
%template(Listint) List<int>;    // Error.   Template already wrapped.

This error is caused because the template expansion results in two identical classes with the same name. This generates a symbol table conflict. Besides, it probably more efficient to only wrap a specific instantiation only once in order to reduce the potential for code bloat.

Since the type system knows how to handle typedef, it is generally not necessary to instantiate different versions of a template for typenames that are equivalent. For instance, consider this code:

致使此錯誤的緣由是模板擴展致使兩個相同名稱的類。這會產生符號表衝突。此外,爲減小代碼膨脹的可能性,僅將特定實例包裝一次可能更有效。

因爲類型系統知道如何處理 typedef,所以一般沒必要爲等效的類型名實例化不一樣版本的模板。例如,考慮如下代碼:

%template(intList) vector<int>;
typedef int Integer;
...
void foo(vector<Integer> *x);

In this case, vector<Integer> is exactly the same type asvector<int>. Any use of Vector<Integer> is mapped back to the instantiation of vector<int> created earlier. Therefore, it is not necessary to instantiate a new class for the type Integer (doing so is redundant and will simply result in code bloat).

When a template is instantiated using %template, information about that class is saved by SWIG and used elsewhere in the program. For example, if you wrote code like this,

在這種狀況下,vector<Integer>vector<int> 的類型徹底相同。對 Vector<Integer> 的任何使用都將映射回先前建立的 vector<int> 的實例化。所以,沒必要爲類型 Integer 實例化新類(這樣作是多餘的,只會致使代碼膨脹)。

使用 %template 實例化模板時,有關該類的信息將由 SWIG 保存並在程序的其餘位置使用。例如,若是你編寫了這樣的代碼,

...
%template(intList) List<int>;
...
class UltraList : public List<int> {
  ...
};

then SWIG knows that List<int> was already wrapped as a class called intList and arranges to handle the inheritance correctly. If, on the other hand, nothing is known about List<int>, you will get a warning message similar to this:

而後 SWIG 知道 List<int> 已經被包裝爲名爲 intList 的類,並安排正確處理繼承。另外一方面,若是對 List<int> 一無所知,則會收到相似如下的警告消息:

example.h:42: Warning 401. Nothing known about class 'List<int >'. Ignored.
example.h:42: Warning 401. Maybe you forgot to instantiate 'List<int >' using %template.

If a template class inherits from another template class, you need to make sure that base classes are instantiated before derived classes. For example:

若是模板類是從另外一個模板類繼承的,則須要確保在派生類以前實例化基類。例如:

template<class T> class Foo {
...
};

template<class T> class Bar : public Foo<T> {
...
};

// Instantiate base classes first
%template(intFoo) Foo<int>;
%template(doubleFoo) Foo<double>;

// Now instantiate derived classes
%template(intBar) Bar<int>;
%template(doubleBar) Bar<double>;

The order is important since SWIG uses the instantiation names to properly set up the inheritance hierarchy in the resulting wrapper code (and base classes need to be wrapped before derived classes). Don't worry--if you get the order wrong, SWIG should generate a warning message.

Occasionally, you may need to tell SWIG about base classes that are defined by templates, but which aren't supposed to be wrapped. Since SWIG is not able to automatically instantiate templates for this purpose, you must do it manually. To do this, simply use the empty template instantiation, that is, %template with no name. For example:

該順序很重要,由於 SWIG 在生成的包裝器代碼中使用實例化名稱設置了繼承層次結構(而且須要在派生類以前包裝基類)。不用擔憂——若是你收到錯誤的順序,SWIG 應該會生成一條警告消息。

有時,你可能須要告訴 SWIG 有關模板定義的基類的信息,但這些基類不該包裝。因爲 SWIG 不能爲此自動實例化模板,所以你必須手動進行。爲此,只需使用空模板實例化,即不帶名稱和 %template。例如:

// Instantiate traits<double, double>, but don't wrap it.
%template() traits<double, double>;

If you have to instantiate a lot of different classes for many different types, you might consider writing a SWIG macro. For example:

若是必須爲許多不一樣的類型實例化許多不一樣的類,則能夠考慮編寫 SWIG 宏。例如:

%define TEMPLATE_WRAP(prefix, T...)
%template(prefix ## Foo) Foo<T >;
%template(prefix ## Bar) Bar<T >;
...
%enddef

TEMPLATE_WRAP(int, int)
TEMPLATE_WRAP(double, double)
TEMPLATE_WRAP(String, char *)
TEMPLATE_WRAP(PairStringInt, std::pair<string, int>)
...

Note the use of a vararg macro for the type T. If this wasn't used, the comma in the templated type in the last example would not be possible.

The SWIG template mechanism does support specialization. For instance, if you define a class like this,

請注意,類型 T 使用了 vararg 宏。若是不使用該宏,則上一個示例中的模板化類型的逗號將不可能。

SWIG 模板機制支持特化。例如,若是你定義這樣的類,

template<> class List<int> {
private:
    int *data;
    int nitems;
    int maxitems;
public:
    List(int max);
    ~List();
    void append(int obj);
    int length();
    int get(int n);
};

then SWIG will use this code whenever the user expands List<int>. In practice, this may have very little effect on the underlying wrapper code since specialization is often used to provide slightly modified method bodies (which are ignored by SWIG ). However, special SWIG directives such as %typemap, %extend, and so forth can be attached to a specialization to provide customization for specific types.

Partial template specialization is partially supported by SWIG . For example, this code defines a template that is applied when the template argument is a pointer.

而後 SWIG 將在用戶每次擴展 List<int> 時使用此代碼。在實踐中,這對底層包裝器代碼的影響可能很小,由於特化一般用於提供通過稍微修改的方法主體(SWIG 會忽略它們)。可是,特殊的 SWIG 指令(如 %typemap%extend 等)能夠附加到特化中,覺得特定類型提供自定義。

SWIG 部分支持部分模板特化。例如,此代碼定義了一個模板,當模板參數爲指針時將應用該模板。

template<class T> class List<T*> {
private:
    T *data;
    int nitems;
    int maxitems;
public:
    List(int max);
    ~List();
    void append(int obj);
    int length();
    T get(int n);
};

SWIG supports both template explicit specialization and partial specialization. Consider:

SWIG 同時支持模板顯式特化和部分特化。考慮下面的狀況

template<class T1, class T2> class Foo { };                     // (1) primary template
template<>                   class Foo<double *, int *> { };    // (2) explicit specialization
template<class T1, class T2> class Foo<T1, T2 *> { };           // (3) partial specialization

SWIG is able to properly match explicit instantiations:

SWIG 能夠正確匹配顯式特化:

Foo<double *, int *>     // explicit specialization matching (2)

SWIG implements template argument deduction so that the following partial specialization examples work just like they would with a C++ compiler:

SWIG 實現了模板參數推導,所以如下部分特化示例的工做方式與使用 C++ 編譯器同樣:

Foo<int *, int *>        // partial specialization matching (3)
Foo<int *, const int *>  // partial specialization matching (3)
Foo<int *, int **>       // partial specialization matching (3)

Member function templates are supported. The underlying principle is the same as for normal templates-- SWIG can't create a wrapper unless you provide more information about types. For example, a class with a member template might look like this:

支持成員函數模板。基本原理與普通模板相同——除非你提供有關類型的更多信息,不然 SWIG 沒法建立包裝器。例如,帶有成員模板的類可能以下所示:

class Foo {
public:
  template<class T> void bar(T x, T y) { ... };
  ...
};

To expand the template, simply use %template inside the class.

爲了擴展模板,在類中使用 %template

class Foo {
public:
  template<class T> void bar(T x, T y) { ... };
  ...
  %template(barint)    bar<int>;
  %template(bardouble) bar<double>;
};

Or, if you want to leave the original class definition alone, just do this:

或者,若是你想初始類定義獨立出來,這樣作:

class Foo {
public:
  template<class T> void bar(T x, T y) { ... };
  ...
};
...
%extend Foo {
  %template(barint)    bar<int>;
  %template(bardouble) bar<double>;
};

or simply

或者簡單點

class Foo {
public:
  template<class T> void bar(T x, T y) { ... };
  ...
};
...

%template(bari) Foo::bar<int>;
%template(bard) Foo::bar<double>;

In this case, the %extend directive is not needed, and %templatedoes exactly the same job, i.e., it adds two new methods to the Foo class.

Note: because of the way that templates are handled, the %template directive must always appear after the definition of the template to be expanded.

Now, if your target language supports overloading, you can even try

在這種狀況下,不須要 %extend 指令,而且 %template 能夠完成徹底相同的工做,即它向 Foo 類添加了兩個新方法。

注意:因爲處理模板的方式,%template 指令必須始終在要擴展的模板定義以後出現。

如今,若是你的目標語言支持重載,你甚至能夠嘗試

%template(bar) Foo::bar<int>;
%template(bar) Foo::bar<double>;

and since the two new wrapped methods have the same name 'bar', they will be overloaded, and when called, the correct method will be dispatched depending on the argument type.

When used with members, the %template directive may be placed in another template class. Here is a slightly perverse example:

而且因爲這兩個新包裝的方法具備相同的名稱 bar,所以它們將被重載,而且在調用時,將根據參數類型調度正確的方法。

與成員一塊兒使用時,能夠將 %template 指令放置在另外一個模板類中。這是一個有點反常的例子:

// A template
template<class T> class Foo {
public:
  // A member template
  template<class S> T bar(S x, S y) { ... };
  ...
};

// Expand a few member templates
%extend Foo {
  %template(bari) bar<int>;
  %template(bard) bar<double>;
}

// Create some wrappers for the template
%template(Fooi) Foo<int>;
%template(Food) Foo<double>;

Miraculously, you will find that each expansion of Foo has member functions bari() and bard() added.

A common use of member templates is to define constructors for copies and conversions. For example:

奇蹟,你會發現 Foo 的每一個擴展都添加了成員函數 bari()bard()

成員模板的常見用法是爲拷貝和轉換定義構造函數。例如:

template<class T1, class T2> struct pair {
  T1 first;
  T2 second;
  pair() : first(T1()), second(T2()) { }
  pair(const T1 &x, const T2 &y) : first(x), second(y) { }
  template<class U1, class U2> pair(
      const pair<U1, U2> &x) : first(x.first), second(x.second) { }
};

This declaration is perfectly acceptable to SWIG , but the constructor template will be ignored unless you explicitly expand it. To do that, you could expand a few versions of the constructor in the template class itself. For example:

SWIG 徹底能夠接受此聲明,可是除非明確擴展它,不然構造函數模板將被忽略。爲此,你能夠在模板類自己中擴展構造函數的幾個版本。例如:

%extend pair {
  %template(pair) pair<T1, T2>;        // Generate default copy constructor
};

When using %extend in this manner, notice how you can still use the template parameters in the original template definition.

Alternatively, you could expand the constructor template in selected instantiations. For example:

當以這種方式使用 %extend 時,請注意如何仍然能夠在原始模板定義中使用模板參數。

另外,你能夠在選定的實例中擴展構造函數模板。例如:

// Instantiate a few versions
%template(pairii) pair<int, int>;
%template(pairdd) pair<double, double>;

// Create a default constructor only
%extend pair<int, int> {
  %template(paird) pair<int, int>;         // Default constructor
};

// Create default and conversion constructors
%extend pair<double, double> {
  %template(paird) pair<double, dobule>;   // Default constructor
  %template(pairc) pair<int, int>;         // Conversion constructor
};

And if your target language supports overloading, then you can try instead:

並且,若是你的目標語言支持重載,你還能夠試試:

// Create default and conversion constructors
%extend pair<double, double> {
  %template(pair) pair<double, dobule>;   // Default constructor
  %template(pair) pair<int, int>;         // Conversion constructor
};

In this case, the default and conversion constructors have the same name. Hence, SWIG will overload them and define an unique visible constructor, that will dispatch the proper call depending on the argument type.

If all of this isn't quite enough and you really want to make someone's head explode, SWIG directives such as %rename, %extend, and %typemap can be included directly in template definitions. For example:

在這種狀況下,默認構造函數和轉換構造函數具備相同的名稱。所以,SWIG 將重載它們並定義一個惟一的可見構造函數,該構造函數將根據參數類型調度適當的調用。

若是全部這些還不夠,而且你真的想讓某人的腦殼爆炸,那麼能夠在模板定義中直接包含諸如 %rename%extend%typemap 之類的 SWIG 指令。例如:

// File : list.h
template<class T> class List {
  ...
public:
  %rename(__getitem__) get(int);
  List(int max);
  ~List();
  ...
  T get(int index);
  %extend {
    char *__str__() {
      /* Make a string representation */
      ...
    }
  }
};

In this example, the extra SWIG directives are propagated to everytemplate instantiation.

It is also possible to separate these declarations from the template class. For example:

在此示例中,額外的 SWIG 指令傳播到每一個模板實例化。

也能夠將這些聲明與模板類分開。例如:

%rename(__getitem__) List::get;
%extend List {
  char *__str__() {
    /* Make a string representation */
    ...
  }
  /* Make a copy */
  T *__copy__() {
    return new List<T>(*$self);
  }
};

...
template<class T> class List {
    ...
    public:
    List() { }
    T get(int index);
    ...
};

When %extend is decoupled from the class definition, it is legal to use the same template parameters as provided in the class definition. These are replaced when the template is expanded. In addition, the %extend directive can be used to add additional methods to a specific instantiation. For example:

%extend 與類定義脫鉤時,使用與類定義中提供的相同的模板參數是合法的。擴展模板時將替換它們。另外,%extend 指令可用於向特定實例添加其餘方法。例如:

%template(intList) List<int>;

%extend List<int> {
    void blah() {
        printf("Hey, I'm an List<int>!\n");
    }
};

SWIG even supports overloaded templated functions. As usual the %template directive is used to wrap templated functions. For example:

SWIG 甚至支持重載函數模板。和往常同樣,%template 指令用於包裝函數模板。例如:

template<class T> void foo(T x) { };
template<class T> void foo(T x, T y) { };

%template(foo) foo<int>;

This will generate two overloaded wrapper methods, the first will take a single integer as an argument and the second will take two integer arguments.

It is even possible to extend a class via %extend with template methods, for example:

這將生成兩個重載的包裝器方法,第一個將使用單個整數做爲參數,第二個將使用兩個整數參數。

甚至能夠經過 %extend 模板方法擴展一個類,例如:

%include <std_string.i>

%inline %{
class ExtendMe {
public:
  template <typename T>
  T do_stuff_impl(int a, T b, double d) {
    return b;
  }
};
%}

%extend ExtendMe {
  template<typename T>
  T do_overloaded_stuff(T b) {
    return $self->do_stuff_impl(0, b, 4.0);
  }
}
%template(do_overloaded_stuff) ExtendMe::do_overloaded_stuff<std::string>;
%template(do_overloaded_stuff) ExtendMe::do_overloaded_stuff<double>;

The wrapped ExtendMe class will then have two (overloaded) methods called do_overloaded_stuff.

Compatibility Note: Extending a class with template methods was added in version 3.0.12

Needless to say, SWIG's template support provides plenty of opportunities to break the universe. That said, an important final point is that SWIG does not perform extensive error checking of templates! Specifically, SWIG does not perform type checking nor does it check to see if the actual contents of the template declaration make any sense. Since the C++ compiler checks this when it compiles the resulting wrapper file, there is no practical reason for SWIG to duplicate this functionality.

包裝好的 ExtendMe 類將有兩個(重載)方法,稱爲 do_overloaded_stuff

注意兼容性:在版本 3.0.12 中添加了使用模板方法擴展類。

不用說,SWIG 的模板支持爲打通宇宙提供了不少機會。就是說,最後的重點是 SWIG 不會執行模板的大量錯誤檢查!具體來講,SWIG 不會執行類型檢查,也不會檢查模板聲明的實際內容是否有意義。因爲 C++ 編譯器在編譯結果包裝器文件時會對此進行檢查,所以 SWIG 沒有實際理由來複制此功能。

template <class T> class OuterTemplateClass {};

// The nested class OuterClass::InnerClass inherits from the template class
// OuterTemplateClass<OuterClass::InnerStruct> and thus the template needs
// to be expanded with %template before the OuterClass declaration.
%template(OuterTemplateClass_OuterClass__InnerStruct)
    OuterTemplateClass<OuterClass::InnerStruct>


// Don't forget to use %feature("flatnested") for OuterClass::InnerStruct and
// OuterClass::InnerClass if the target language doesn't support nested classes.
class OuterClass {
    public:
        // Forward declarations:
        struct InnerStruct;
        class InnerClass;
};

struct OuterClass::InnerStruct {};

// Expanding the template at this point with %template is too late as the
// OuterClass::InnerClass declaration is processed inside OuterClass.

class OuterClass::InnerClass : public OuterTemplateClass<InnerStruct> {};

Compatibility Note: The first implementation of template support relied heavily on macro expansion in the preprocessor. Templates have been more tightly integrated into the parser and type system in SWIG-1.3.12 and the preprocessor is no longer used. Code that relied on preprocessing features in template expansion will no longer work. However, SWIG still allows the # operator to be used to generate a string from a template argument.

Compatibility Note: In earlier versions of SWIG , the %templatedirective introduced a new class name. This name could then be used with other directives. For example:

注意兼容性:模板支持的第一個實如今很大程度上依賴於預處理器中的宏擴展。模板已在 SWIG-1.3.12 中更緊密地集成到解析器和類型系統中,而且再也不使用預處理器。依靠模板擴展中的預處理功能的代碼將再也不起做用。可是,SWIG 仍然容許使用 # 運算符從模板參數生成字符串。

注意兼容性:在 SWIG 的早期版本中,%template 指令引入了新的類名。而後,該名稱能夠與其餘指令一塊兒使用。例如:

%template(vectori) vector<int>;
%extend vectori {
    void somemethod() { }
};

This behavior is no longer supported. Instead, you should use the original template name as the class name. For example:

再也不支持這種行爲。可是,你要使用原始模板名做爲類名。例如:

%template(vectori) vector<int>;
%extend vector<int> {
    void somemethod() { }
};

Similar changes apply to typemaps and other customization features.

相似的更改適用於類型映射和其餘自定義功能。

6.19 命名空間

Support for C++ namespaces is comprehensive, but by default simple, however, some target languages can turn on more advanced namespace support via the nspace feature, described later. Code within unnamed namespaces is ignored as there is no external access to symbols declared within the unnamed namespace. Before detailing the default implementation for named namespaces, it is worth noting that the semantics of C++ namespaces is extremely non-trivial--especially with regard to the C++ type system and class machinery. At a most basic level, namespaces are sometimes used to encapsulate common functionality. For example:

對 C++ 命名空間的支持是全面的,默認狀況下卻很簡單,可是某些目標語言能夠經過 nspace 功能打開更高級的命名空間支持,稍後會描述。未命名的命名空間中的代碼將被忽略,由於沒法從外部訪問未命名的命名空間中聲明的符號。在詳細說明命名空間的默認實現以前,值得注意的是 C++ 命名空間的語義很是重要——特別是對於 C++ 類型系統和類機制而言。在最基本的級別上,命名空間有時用於封裝通用功能。例如:

namespace math {
  double sin(double);
  double cos(double);

  class Complex {
    double im, re;
  public:
    ...
  };
  ...
};

Members of the namespace are accessed in C++ by prepending the namespace prefix to names. For example:

在 C++ 中,經過將命名空間前綴放在名稱以前,能夠訪問命名空間的成員。例如:

double x = math::sin(1.0);
double magnitude(math::Complex *c);
math::Complex c;
...

At this level, namespaces are relatively easy to manage. However, things start to get very ugly when you throw in the other ways a namespace can be used. For example, selective symbols can be exported from a namespace with using.

在此級別上,命名空間相對易於管理。可是,當你以其餘方式使用命名空間時,事情變得很是醜陋。例如,可使用 using 從命名空間中導出選擇的符號。

using math::Complex;
double magnitude(Complex *c);       // Namespace prefix stripped

Similarly, the contents of an entire namespace can be made available like this:

相似地,整個命名空間的內容能夠這樣提供:

using namespace math;
double x = sin(1.0);
double magnitude(Complex *c);

Alternatively, a namespace can be aliased:

或者,命名空間能夠這樣表示:

namespace M = math;
double x = M::sin(1.0);
double magnitude(M::Complex *c);

Using combinations of these features, it is possible to write head-exploding code like this:

使用這些功能的組合,能夠編寫以下燒腦的代碼:

namespace A {
  class Foo {
  };
}

namespace B {
  namespace C {
    using namespace A;
  }
  typedef C::Foo FooClass;
}

namespace BIGB = B;

namespace D {
  using BIGB::FooClass;
  class Bar : public FooClass {
  }
};

class Spam : public D::Bar {
};

void evil(A::Foo *a, B::FooClass *b, B::C::Foo *c, BIGB::FooClass *d,
          BIGB::C::Foo *e, D::FooClass *f);

Given the possibility for such perversion, it's hard to imagine how every C++ programmer might want such code wrapped into the target language. Clearly this code defines three different classes. However, one of those classes is accessible under at least six different names!

SWIG fully supports C++ namespaces in its internal type system and class handling code. If you feed SWIG the above code, it will be parsed correctly, it will generate compilable wrapper code, and it will produce a working scripting language module. However, the default wrapping behavior is to flatten namespaces in the target language. This means that the contents of all namespaces are merged together in the resulting scripting language module. For example, if you have code like this,

考慮到這種變態的可能性,很難想象 C++ 程序員可能會如何將這樣的代碼包裝到目標語言中。顯然,此代碼定義了三個不一樣的類。可是,能夠至少使用六個不一樣的名稱來訪問其中一個類!

SWIG 在其內部類型系統和類處理代碼中徹底支持 C++ 命名空間。若是將上面的代碼提供給 SWIG ,它將被正確地解析,它將生成可編譯的包裝器代碼,而且將產生一個有效的腳本語言模塊。可是,默認的包裝行爲是將目標語言中的命名空間展平。這意味着全部命名空間的內容在結果腳本語言模塊中合併在一塊兒。例如,若是你有這樣的代碼,

%module foo
namespace foo {
  void bar(int);
  void spam();
}

namespace bar {
  void blah();
}

then SWIG simply creates three wrapper functions bar(), spam(), and blah() in the target language. SWIG does not prepend the names with a namespace prefix nor are the functions packaged in any kind of nested scope.

There is some rationale for taking this approach. Since C++ namespaces are often used to define modules in C++, there is a natural correlation between the likely contents of a SWIG module and the contents of a namespace. For instance, it would not be unreasonable to assume that a programmer might make a separate extension module for each C++ namespace. In this case, it would be redundant to prepend everything with an additional namespace prefix when the module itself already serves as a namespace in the target language. Or put another way, if you want SWIG to keep namespaces separate, simply wrap each namespace with its own SWIG interface.

Because namespaces are flattened, it is possible for symbols defined in different namespaces to generate a name conflict in the target language. For example:

而後 SWIG 只需用目標語言建立三個包裝函數 bar()spam()blah()。SWIG 不會在名稱前添加命名空間前綴,函數也不會打包在任何嵌套做用域中。

採用這種方法有一些理由。因爲 C++ 命名空間一般用於在 C++ 中定義模塊,所以可能 SWIG 模塊的內容與命名空間的內容之間存在天然的關聯。例如,假設程序員能夠爲每一個 C++ 命名空間建立一個單獨的擴展模塊,這並不是沒有道理。在這種狀況下,當模塊自己已經用做目標語言中的命名空間時,在全部內容前面都添加一個額外的命名空間前綴將是多餘的。或換一種說法,若是你但願 SWIG 將命名空間分隔開,只需將每一個命名空間都用其本身的 SWIG 接口包裝便可。

因爲命名空間被展平,所以在不一樣命名空間中定義的符號可能會在目標語言中產生名稱衝突。例如:

namespace A {
  void foo(int);
}
namespace B {
  void foo(double);
}

When this conflict occurs, you will get an error message that resembles this:

發生此衝突時,你將收到相似於如下內容的錯誤消息:

example.i:26. Error. 'foo' is multiply defined in the generated target language module.
example.i:23. Previous declaration of 'foo'

To resolve this error, simply use %rename to disambiguate the declarations. For example:

要解決此錯誤,只需使用 %rename 來消除聲明的歧義。例如:

%rename(B_foo) B::foo;
...
namespace A {
  void foo(int);
}
namespace B {
  void foo(double);     // Gets renamed to B_foo
}

Similarly, %ignore can be used to ignore declarations.

using declarations do not have any effect on the generated wrapper code. They are ignored by SWIG language modules and they do not result in any code. However, these declarations areused by the internal type system to track type-names. Therefore, if you have code like this:

一樣,%ignore 可用於忽略聲明。

using 聲明對生成的包裝器代碼沒有任何影響。SWIG 語言模塊將忽略它們,而且不會產生任何代碼。可是,這些聲明由內部類型系統用於跟蹤類型名稱。所以,若是你有這樣的代碼:

namespace A {
  typedef int Integer;
}
using namespace A;
void foo(Integer x);

SWIG knows that Integer is the same as A::Integer which is the same as int.

Namespaces may be combined with templates. If necessary, the%template directive can be used to expand a template defined in a different namespace. For example:

SWIG 知道 IntegerA::Integer 相同,後者與 int 相同。

命名空間能夠與模板結合使用。若有必要,可使用 %template 指令擴展在不一樣命名空間中定義的模板。例如:

namespace foo {
    template<typename T> T max(T a, T b) { return a > b ? a : b; }
}

using foo::max;

%template(maxint)   max<int>;           // Okay.
%template(maxfloat) foo::max<float>;    // Okay (qualified name).

namespace bar {
    using namespace foo;
    %template(maxdouble)  max<double>;    // Okay.
}

The combination of namespaces and other SWIG directives may introduce subtle scope-related problems. The key thing to keep in mind is that all SWIG generated wrappers are produced in the global namespace. Symbols from other namespaces are always accessed using fully qualified names---names are never imported into the global space unless the interface happens to do so with a using declaration. In almost all cases, SWIG adjusts typenames and symbols to be fully qualified. However, this is not done in code fragments such as function bodies, typemaps, exception handlers, and so forth. For example, consider the following:

命名空間和其餘 SWIG 指令的組合可能會引發與做用域相關的微妙問題。關鍵要記住,全部 SWIG 生成的包裝器都是在全局命名空間中生成的。老是使用徹底限定的名稱來訪問來自其餘命名空間的符號——除非接口碰巧使用 using 聲明,不然名稱永遠不會導入全局空間。在幾乎全部狀況下,SWIG 都會將類型名和符號調整爲徹底合法的。可是,在代碼片斷(例如函數體、類型映射、異常處理程序等)中並未作到這一點。例如,考慮如下內容:

namespace foo {
  typedef int Integer;
  class bar {
    public:
      ...
  };
}

%extend foo::bar {
  Integer add(Integer x, Integer y) {
    Integer r = x + y;        // Error. Integer not defined in this scope
    return r;
  }
};

In this case, SWIG correctly resolves the added method parameters and return type to foo::Integer. However, since function bodies aren't parsed and such code is emitted in the global namespace, this code produces a compiler error about Integer. To fix the problem, make sure you use fully qualified names. For example:

在這種狀況下,SWIG 會正確解析添加的方法參數,並將類型返回爲 foo::Integer。可是,因爲未解析函數體,而且此類代碼在全局命名空間中發出,所以此代碼會產生有關 Integer 的編譯器錯誤。要解決此問題,請確保使用徹底限定的名稱。例如:

%extend foo::bar {
  Integer add(Integer x, Integer y) {
    foo::Integer r = x + y;        // Ok.
    return r;
  }
};

Note: SWIG does not propagate using declarations to the resulting wrapper code. If these declarations appear in an interface, they should also appear in any header files that might have been included in a %{ ... %} section. In other words, don't insert extrausing declarations into a SWIG interface unless they also appear in the underlying C++ code.

Note: Code inclusion directives such as %{ ... %} or %inline %{ ... %} should not be placed inside a namespace declaration. The code emitted by these directives will not be enclosed in a namespace and you may get very strange results. If you need to use namespaces with these directives, consider the following:

注意:SWIG 不會將 using 聲明傳播到包裝器代碼。若是這些聲明出如今接口中,它們也應該出如今任何可能包含 %{...%} 部分的頭文件中。換句話說,除非在基礎 C++ 代碼中也出現了多餘的 using 聲明,不然不要在 SWIG 接口中插入它們。

注意:不該將代碼包含指令,例如 %{...%}%inline %{...%} 放在命名空間聲明中。這些指令發出的代碼不會包含在命名空間中,你可能會獲得很是奇怪的結果。若是須要經過這些指令使用命名空間,請考慮如下事項:

// Good version
%inline %{
namespace foo {
  void bar(int) { ... }
  ...
}
%}

// Bad version.  Emitted code not placed in namespace.
namespace foo {
%inline %{
  void bar(int) { ... }   /* I'm bad */
  ...
  %}
}

Note: When the %extend directive is used inside a namespace, the namespace name is included in the generated functions. For example, if you have code like this,

注意:在命名空間中使用 %extend 指令時,命名空間名稱包含在生成的函數中。例如,若是你有這樣的代碼,

namespace foo {
  class bar {
    public:
      %extend {
        int blah(int x);
      };
  };
}

the added method blah() is mapped to a function int foo_bar_blah(foo::bar *self, int x). This function resides in the global namespace.

Note: Although namespaces are flattened in the target language, the SWIG generated wrapper code observes the same namespace conventions as used in the input file. Thus, if there are no symbol conflicts in the input, there will be no conflicts in the generated code.

Note: In the same way that no resolution is performed on parameters, a conversion operator name must match exactly to how it is defined. Do not change the qualification of the operator. For example, suppose you had an interface like this:

添加的方法 blah() 映射到函數 int foo_bar_blah(foo::bar *self, int x)。該函數位於全局命名空間中。

注意:儘管命名空間使用目標語言進行了展平,可是 SWIG 生成的包裝器代碼遵照與輸入文件中相同的命名空間約定。所以,若是輸入中沒有符號衝突,則生成的代碼中將沒有衝突。

注意:與不對參數執行任何解析同樣,轉換運算符名稱必須與定義方式徹底匹配。請勿更改運算符的限定詞。例如,假設你有一個像這樣的接口:

namespace foo {
  class bar;
  class spam {
    public:
    ...
    operator bar();      // Conversion of spam -> bar
    ...
  };
}

The following is how the feature is expected to be written for a successful match:

如下是成功匹配指望的功能編寫方式:

%rename(tofoo) foo::spam::operator bar();

The following does not work as no namespace resolution is performed in the matching of conversion operator names:

因爲在轉換運算符名稱的匹配中未執行命名空間解析,所以如下操做不起做用:

%rename(tofoo) foo::spam::operator foo::bar();

Note, however, that if the operator is defined using a qualifier in its name, then the feature must use it too...

可是請注意,若是使用名稱中的限定符定義了運算符,則該功能也必須使用它。

%rename(tofoo) foo::spam::operator bar();      // will not match
%rename(tofoo) foo::spam::operator foo::bar(); // will match
namespace foo {
  class bar;
  class spam {
    public:
    ...
    operator foo::bar();
    ...
  };
}

Compatibility Note: Versions of SWIG prior to 1.3.32 were inconsistent in this approach. A fully qualified name was usually required, but would not work in some situations.

Note: The flattening of namespaces is only intended to serve as a basic namespace implementation. None of the target language modules are currently programmed with any namespace awareness. In the future, language modules may or may not provide more advanced namespace support.

注意兼容性:1.3.32 以前的 SWIG 版本在此方法中不一致。一般須要一個徹底限定的名稱,但在某些狀況下不起做用。

注意:命名空間的展平僅旨在用做基本的命名空間實現。當前,目標語言模塊都沒有任何使用命名空間進行編程的意識。未來,語言模塊可能會或可能不會提供更高級的命名空間支持。

6.19.1 針對命名空間的 nspace 功能

Some target languages provide support for the nspace feature. The feature can be applied to any class, struct, union or enum declared within a named namespace. The feature wraps the type within the target language specific concept of a namespace, for example, a Java package or C# namespace. Please see the language specific sections to see if the target language you are interested in supports the nspace feature.

The feature is demonstrated below for C# using the following example:

某些目標語言爲 nspace 功能提供支持。該功能能夠應用於命名空間中聲明的任何類、結構體、共用體或枚舉。該功能將類型包裝在目標語言中對應命名空間的特定概念內,例如 Java 包或 C# 命名空間。請參閱特定於語言的部分,以查看你感興趣的目標語言是否支持 nspace 功能。

下面使用如下示例針對 C# 演示了該功能:

%feature("nspace") MyWorld::Material::Color;
%nspace MyWorld::Wrapping::Color; // %nspace is a macro for %feature("nspace")

namespace MyWorld {
  namespace Material {
    class Color {
    ...
    };
  }
  namespace Wrapping {
    class Color {
    ...
    };
  }
}

Without the nspace feature directives above or %rename, you would get the following warning resulting in just one of the Color classes being available for use from the target language:

若是沒有上面和 nspace 功能指令或 %rename,你將獲得如下警告,致使只有一種 Color 類能夠從目標語言中使用:

example.i:9: Error: 'Color' is multiply defined in the generated target language module.
example.i:5: Error: Previous declaration of 'Color'

With the nspace feature the two Color classes are wrapped into the equivalent C# namespaces. A fully qualified constructor call of each these two types in C# is then:

經過 nspace 功能,兩個 Color 類被包裝到等效的 C# 命名空間中。而後,在 C# 中對這兩種類型的徹底限定構造函數調用以下:

MyWorld.Material.Color materialColor = new MyWorld.Material.Color();
MyWorld.Wrapping.Color wrappingColor = new MyWorld.Wrapping.Color();

Note that the nspace feature does not apply to variables and functions simply declared in a namespace. For example, the following symbols cannot co-exist in the target language without renaming. This may change in a future version.

注意,nspace 功能不適用於僅在命名空間中聲明的變量和函數。例如,如下符號不能在不重命名的狀況下在目標語言中共存。這可能會在未來的版本中更改。

namespace MyWorld {
  namespace Material {
    int quantity;
    void dispatch();
  }
  namespace Wrapping {
    int quantity;
    void dispatch();
  }
}

Compatibility Note: The nspace feature was first introduced in SWIG-2.0.0.

注意兼容性nspace 功能在 SWIG-2.0.0 中首次引入。

6.20 在命名空間中重命名模板類型

As has been mentioned, when %rename includes parameters, the parameter types must match exactly (no typedef or namespace resolution is performed). SWIG treats templated types slightly differently and has an additional matching rule so unlike non-templated types, an exact match is not always required. If the fully qualified templated type is specified, it will have a higher precedence over the generic template type. In the example below, the generic template type is used to rename to bbb and the fully qualified type is used to rename to ccc.

如前所述,當 %rename 包含參數時,參數類型必須徹底匹配(不執行 typedef 或命名空間解析)。SWIG 對模板化類型的處理略有不一樣,而且具備其餘匹配規則,所以與非模板化類型不一樣,不必定老是須要徹底匹配。若是指定了徹底限定的模板化類型,則其優先級高於通用模板類型。在下面的示例中,通用模板類型重命名爲 bbb,徹底限定類型重命名爲 ccc

%rename(bbb) Space::ABC::aaa(T t);                  // will match but with lower precedence than ccc
%rename(ccc) Space::ABC<Space::XYZ>::aaa(Space::XYZ t);// will match but with higher precedence than bbb

namespace Space {
  class XYZ {};
  template<typename T> struct ABC {
    void aaa(T t) {}
  };
}
%template(ABCXYZ) Space::ABC<Space::XYZ>;

It should now be apparent that there are many ways to achieve a renaming with %rename. This is demonstrated by the following two examples, which are effectively the same as the above example. Below shows how %rename can be placed inside a namespace.

如今應該很明顯,有不少方法可使用 %rename 進行重命名。如下兩個示例能夠證實這一點,這些示例實際上與上述示例相同。下面顯示瞭如何將 %rename 放在命名空間中。

namespace Space {
  %rename(bbb) ABC::aaa(T t);                     // will match but with lower precedence than ccc
  %rename(ccc) ABC<Space::XYZ>::aaa(Space::XYZ t);// will match but with higher precedence than bbb
  %rename(ddd) ABC<Space::XYZ>::aaa(XYZ t);       // will not match
}

namespace Space {
  class XYZ {};
  template<typename T> struct ABC {
    void aaa(T t) {}
  };
}
%template(ABCXYZ) Space::ABC<Space::XYZ>;

Note that ddd does not match as there is no namespace resolution for parameter types and the fully qualified type must be specified for template type expansion. The following example shows how %rename can be placed within %extend.

請注意,因爲參數類型沒有命名空間解析,而且必須爲模板類型擴展指定徹底限定的類型,所以 ddd 不匹配。如下示例顯示如何將 %rename 放在 %extend 中。

namespace Space {
  %extend ABC {
    %rename(bbb) aaa(T t);         // will match but with lower precedence than ccc
  }
  %extend ABC<Space::XYZ> {
    %rename(ccc) aaa(Space::XYZ t);// will match but with higher precedence than bbb
    %rename(ddd) aaa(XYZ t);       // will not match
  }
}

namespace Space {
  class XYZ {};
  template<typename T> struct ABC {
    void aaa(T t) {}
  };
}
%template(ABCXYZ) Space::ABC<Space::XYZ>;

6.21 異常規範

When C++ programs utilize exceptions, exceptional behavior is sometimes specified as part of a function or method declaration. For example:

當 C++ 程序利用異常時,有時會將異常行爲指定爲函數或方法聲明的一部分。例如:

class Error { };

class Foo {
public:
    ...
    void blah() throw(Error);
    ...
};

If an exception specification is used, SWIG automatically generates wrapper code for catching the indicated exception and, when possible, rethrowing it into the target language, or converting it into an error in the target language otherwise. For example, in Python, you can write code like this:

若是使用了異常規範,SWIG 會自動生成包裝器代碼,以捕獲指示的異常,並在可能的狀況下,將其在目標語言中從新拋出,不然將其轉換爲目標語言中的錯誤。例如,在 Python 中,你能夠編寫以下代碼:

f = Foo()
try:
    f.blah()
except Error, e:
    # e is a wrapped instance of "Error"

Details of how to tailor code for handling the caught C++ exception and converting it into the target language's exception/error handling mechanism is outlined in the "throws" typemap section.

Since exception specifications are sometimes only used sparingly, this alone may not be enough to properly handle C++ exceptions. To do that, a different set of special SWIG directives are used. Consult the "Exception handling with %exception" section for details. The next section details a way of simulating an exception specification or replacing an existing one.

拋出類型映射中概述了有關如何自定義代碼以處理捕獲的 C++ 異常,並將其轉換爲目標語言的異常/錯誤處理機制的詳細信息。

因爲有時僅不多使用異常規範,所以僅憑其自己可能不足以正確處理 C++ 異常。爲此,使用了一組不一樣的特殊 SWIG 指令。有關詳細信息,請查閱使用 %exception 處理異常部分。下一節將詳細介紹一種模擬異常規範或替換現有規範的方法。

6.22 用 %catches 處理異常

Exceptions are automatically handled for methods with an exception specification. Similar handling can be achieved for methods without exception specifications through the %catches feature. It is also possible to replace any declared exception specification using the %catches feature. In fact, %catches uses the same "throws" typemaps that SWIG uses for exception specifications in handling exceptions. The %catches feature must contain a list of possible types that can be thrown. For each type that is in the list, SWIG will generate a catch handler, in the same way that it would for types declared in the exception specification. Note that the list can also include the catch all specification "...". For example,

帶有異常規範的方法會自動處理異常。經過 %catches 功能,能夠爲沒有異常規範的方法實現相似的處理。也可使用 %catches 功能替換任何聲明的異常規範。實際上,%catches 使用的拋出類型映射與 SWIG 處理異常的異常規範相同。%catches 功能必須包含可能拋出的類型列表。對於列表中的每種類型,SWIG 都將生成捕獲處理程序,並與異常規範中聲明的類型一致。請注意,該列表還能夠包含捕獲全部規範 ...。例如,

struct EBase { virtual ~EBase(); };
struct Error1 : EBase { };
struct Error2 : EBase { };
struct Error3 : EBase { };
struct Error4 : EBase { };

%catches(Error1, Error2, ...) Foo::bar();
%catches(EBase) Foo::blah();

class Foo {
public:
    ...
    void bar();
    void blah() throw(Error1, Error2, Error3, Error4);
    ...
};

For the Foo::bar() method, which can throw anything, SWIG will generate catch handlers for Error1, Error2 as well as a catch all handler (...). Each catch handler will convert the caught exception and convert it into a target language error/exception. The catch all handler will convert the caught exception into an unknown error/exception.

Without the %catches feature being attached to Foo::blah(), SWIG will generate catch handlers for all of the types in the exception specification, that is, Error1, Error2, Error3, Error4. However, with the %catches feature above, just a single catch handler for the base class, EBase will be generated to convert the C++ exception into a target language error/exception.

對於能夠拋出任何內容的 Foo::bar() 方法,SWIG 將爲 Error1Error2 以及全部(...)生成捕獲處理程序。每一個 catch 處理程序都將轉換捕獲的異常,並將其轉換爲目標語言錯誤/異常。捕獲全部會將捕獲的異常轉換爲未知的錯誤/異常。

沒有在 Foo::blah() 上附加 %catches 功能,SWIG 將爲異常規範中的全部類型(即 Error1Error2Error3Error4)生成捕獲處理程序。可是,使用上面的 %catches 功能,僅生成基類 EBase 的單個捕獲處理程序便可將 C++ 異常轉換爲目標語言錯誤/異常。

6.23 成員指針

Starting with SWIG-1.3.7, there is limited parsing support for pointers to C++ class members. For example:

從 SWIG-1.3.7 開始,能夠有限地支持對指向 C++ 類成員的指針的解析。例如:

double do_op(Object *o, double (Object::*callback)(double, double));
extern double (Object::*fooptr)(double, double);
%constant double (Object::*FOO)(double, double) = &Object::foo;

Although these kinds of pointers can be parsed and represented by the SWIG type system, few language modules know how to handle them due to implementation differences from standard C pointers. Readers are strongly advised to consult an advanced text such as the "The Annotated C++ Manual" for specific details.

When pointers to members are supported, the pointer value might appear as a special string like this:

儘管能夠經過 SWIG 類型系統解析和表示這些類型的指針,可是因爲與標準 C 指針的實現有所不一樣,不多有語言模塊知道如何處理它們。強烈建議讀者閱讀高級教程(例如《The Annotated C++ Manual》)以瞭解詳細信息。

當支持指向成員的指針時,指針值可能顯示爲特殊字符串,以下所示:

>>> print example.FOO
_ff0d54a800000000_m_Object__f_double_double__double
>>>

In this case, the hexadecimal digits represent the entire value of the pointer which is usually the contents of a small C++ structure on most machines.

SWIG's type-checking mechanism is also more limited when working with member pointers. Normally SWIG tries to keep track of inheritance when checking types. However, no such support is currently provided for member pointers.

在這種狀況下,十六進制數字表示指針的整個值,該值一般是大多數計算機上小型 C++ 結構體的內容。

在使用成員指針時,SWIG 的類型檢查機制也受到更多限制。一般,SWIG 在檢查類型時會嘗試跟蹤繼承。可是,當前沒有爲成員指針提供這種支持。

6.24 智能指針與 operator->()

In some C++ programs, objects are often encapsulated by smart-pointers or proxy classes. This is sometimes done to implement automatic memory management (reference counting) or persistence. Typically a smart-pointer is defined by a template class where the -> operator has been overloaded. This class is then wrapped around some other class. For example:

在某些 C++ 程序中,對象一般由智能指針或代理類封裝。有時這樣作是爲了實現自動內存管理(引用計數)或持久性。一般,智能指針是由模板類定義的,在該模板類中已重載了 -> 運算符。而後,將此類包裝在其餘一些類別上。例如:

// Smart-pointer class
template<class T> class SmartPtr {
    T *pointee;
public:
    SmartPtr(T *p) : pointee(p) { ... }
    T *operator->() {
        return pointee;
    }
    ...
};

// Ordinary class
class Foo_Impl {
public:
    int x;
    virtual void bar();
    ...
};

// Smart-pointer wrapper
typedef SmartPtr<Foo_Impl> Foo;

// Create smart pointer Foo
Foo make_Foo() {
    return SmartPtr<Foo_Impl>(new Foo_Impl());
}

// Do something with smart pointer Foo
void do_something(Foo f) {
    printf("x = %d\n", f->x);
    f->bar();
}

// Call the wrapped smart pointer proxy class in the target language 'Foo'
%template(Foo) SmartPtr<Foo_Impl>;

A key feature of this approach is that by defining operator-> the methods and attributes of the object wrapped by a smart pointer are transparently accessible. For example, expressions such as these (from the previous example),

這種方法的關鍵特徵是經過定義 operator->,能夠透明地訪問由智能指針所包裝對象的方法和屬性。例如,這些表達式(來自上一個示例)

f->x
f->bar()

are transparently mapped to the following

透明地映射到

(f.operator->())->x;
(f.operator->())->bar();

When generating wrappers, SWIG tries to emulate this functionality to the extent that it is possible. To do this, wheneveroperator->() is encountered in a class, SWIG looks at its returned type and uses it to generate wrappers for accessing attributes of the underlying object. For example, wrapping the above code produces wrappers like this:

在生成包裝器時,SWIG 嘗試儘量的模擬此功能。爲此,每當在類中遇到 operator->() 時,SWIG 都會查看其返回的類型,並使用它生成用於訪問底層對象屬性的包裝器。例如,包裝上面的代碼將產生以下包裝:

int Foo_x_get(Foo *f) {
  return (*f)->x;
}
void Foo_x_set(Foo *f, int value) {
  (*f)->x = value;
}
void Foo_bar(Foo *f) {
  (*f)->bar();
}

These wrappers take a smart-pointer instance as an argument, but dereference it in a way to gain access to the object returned byoperator->(). You should carefully compare these wrappers to those in the first part of this chapter (they are slightly different).

The end result is that access looks very similar to C++. For example, you could do this in Python:

這些包裝器將智能指針實例做爲參數,可是以某種方式解引用它以得到對 operator->() 返回對象的訪問。你應該將這些包裝器與本章第一部分中的包裝器進行仔細比較(它們略有不一樣)。

最終結果是訪問看起來與 C++ 很是類似。例如,你能夠在 Python 中執行此操做:

>>> f = make_Foo()
>>> print f.x
0
>>> f.bar()
>>>

When generating wrappers through a smart-pointer, SWIG tries to generate wrappers for all methods and attributes that might be accessible through operator->(). This includes any methods that might be accessible through inheritance. However, there are a number of restrictions:

  • Member variables and methods are wrapped through a smart pointer. Enumerations, constructors, and destructors are not wrapped.
  • If the smart-pointer class and the underlying object both define a method or variable of the same name, then the smart-pointer version has precedence. For example, if you have this code

經過智能指針生成包裝器時,SWIG 會嘗試爲可能經過 operator->() 訪問的全部方法和屬性生成包裝器。這包括能夠經過繼承訪問的任何方法。可是,有許多限制:

  • 成員變量和方法經過智能指針包裝。枚舉、構造函數和析構函數不會被包裝。
  • 若是智能指針類和底層對象都定義了相同名稱的方法或變量,則智能指針版本具備優先級別。例如,若是你有此代碼
class Foo {
public:
  int x;
};

class Bar {
public:
  int x;
  Foo *operator->();
};

then the wrapper for Bar::x accesses the x defined in Bar, and not the x defined in Foo.

If your intent is to only expose the smart-pointer class in the interface, it is not necessary to wrap both the smart-pointer class and the class for the underlying object. However, you must still tell SWIG about both classes if you want the technique described in this section to work. To only generate wrappers for the smart-pointer class, you can use the %ignore directive. For example:

那麼 Bar::x 的包裝器訪問 Bar 定義的 x,而不訪問 Foo 定義的 x

若是你的目的只是在接口中公開智能指針類,則沒必要同時包裝智能指針類和底層對象的類。可是,若是你但願本節中介紹的技術起做用,則仍必須將兩個類都告訴 SWIG。要僅爲智能指針類生成包裝器,可使用 %ignore 指令。例如:

%ignore Foo;
class Foo {       // Ignored
};

class Bar {
public:
  Foo *operator->();
  ...
};

Alternatively, you can import the definition of Foo from a separate file using %import.

Note: When a class defines operator->(), the operator itself is wrapped as a method __deref__(). For example:

另外,你可使用 %import 從一個單獨的文件中導入 Foo 的定義。

注意:當一個類定義 operator->() 時,運算符自己被包裝爲方法 __deref __()。例如:

f = Foo()               # Smart-pointer
p = f.__deref__()       # Raw pointer from operator->

Note: To disable the smart-pointer behavior, use %ignore to ignore operator->(). For example:

注意:爲了取消智能指針的行爲,使用 %ignore 忽略 operator->()。例如:

%ignore Bar::operator->;

Note: Smart pointer support was first added in SWIG-1.3.14.

注意:對智能指針的支持在 SWIG-1.3.14 中首次添加。

6.25 C++ 引用計數對象——ref / unref 功能

Another similar idiom in C++ is the use of reference counted objects. Consider for example:

C++ 中的另外一個慣用法是引用計數對象的使用。考慮例如:

class RCObj  {
  // implement the ref counting mechanism
  int add_ref();
  int del_ref();
  int ref_count();

public:
  virtual ~RCObj() = 0;

  int ref() const {
    return add_ref();
  }

  int unref() const {
    if (ref_count() == 0 || del_ref() == 0 ) {
      delete this;
      return 0;
    }
    return ref_count();
  }
};


class A : RCObj {
public:
  A();
  int foo();
};


class B {
  A *_a;

public:
  B(A *a) : _a(a) {
    a->ref();
  }

  ~B() {
    a->unref();
  }
};

int main() {
  A *a  = new A();       // (count: 0)
  a->ref();           // 'a' ref here (count: 1)

  B *b1 = new B(a);   // 'a' ref here (count: 2)
  if (1 + 1 == 2) {
    B *b2 = new B(a); // 'a' ref here (count: 3)
    delete b2;        // 'a' unref, but not deleted (count: 2)
  }

  delete b1;          // 'a' unref, but not deleted (count: 1)
  a->unref();         // 'a' unref and deleted (count: 0)
}

In the example above, the 'A' class instance 'a' is a reference counted object, which can't be deleted arbitrarily since it is shared between the objects 'b1' and 'b2'. 'A' is derived from a Reference Counted Object 'RCObj', which implements the ref/unref idiom.

To tell SWIG that 'RCObj' and all its derived classes are reference counted objects, use the "ref" and "unref" features. These are also available as %refobject and %unrefobject, respectively. For example:

在上面的示例中,A 類實例 a 是一個引用計數的對象,因爲它在對象 b1b2 之間共享,所以不能任意刪除。A 源自「引用計數對象」(RCObj),該引用實現了 ref / unref 慣用法。

要告訴 SWIG RCObj 及其全部派生類都是引用計數對象,請使用 refunref 功能。這些也能夠分別以 %refobject%unrefobject 的形式得到。例如:

%module example
...

%feature("ref")   RCObj "$this->ref();"
%feature("unref") RCObj "$this->unref();"

%include "rcobj.h"
%include "A.h"
...

where the code passed to the "ref" and "unref" features will be executed as needed whenever a new object is passed to python, or when python tries to release the proxy object instance, respectively.

On the python side, the use of a reference counted object is no different to any other regular instance:

其中,每當將新對象傳遞給 python,或 python 嘗試釋放代理對象實例時,傳遞給 refunref 功能的代碼將根據須要執行。

在 python 方面,引用計數對象的使用與任何其餘常規實例沒有什麼不一樣:

def create_A():
    a = A()         # SWIG ref 'a' - new object is passed to python (count: 1)
    b1 = B(a)       # C++ ref 'a (count: 2)
    if 1 + 1 == 2:
        b2 = B(a)   # C++ ref 'a' (count: 3)
    return a        # 'b1' and 'b2' are released and deleted, C++ unref 'a' twice (count: 1)

a = create_A()      # (count: 1)
exit                # 'a' is released, SWIG unref 'a' called in the destructor wrapper (count: 0)

Note that the user doesn't explicitly need to call 'a->ref()' nor 'a->unref()' (and neither 'delete a'). Instead, SWIG takes cares of executing the "ref" and "unref" calls as needed. If the user doesn't specify the "ref/unref" feature for a type, SWIG will produce code equivalent to defining these features:

請注意,用戶不須要顯式調用 a->ref()a->unref()(也無需 delete a)。相反,SWIG 會根據須要執行 refunref 調用。若是用戶未爲類型指定 ref / unref 功能,SWIG 將產生與定義這些功能等效的代碼:

%feature("ref")   ""
%feature("unref") "delete $this;"

In other words, SWIG will not do anything special when a new object is passed to python, and it will always 'delete' the underlying object when python releases the proxy instance.

The %newobject feature is designed to indicate to the target language that it should take ownership of the returned object. When used in conjunction with a type that has the "ref" feature associated with it, it additionally emits the code in the "ref" feature into the C++ wrapper. Consider wrapping the following factory function in addition to the above:

換句話說,將新對象傳遞給 python 時,SWIG 不會作任何特殊的事情,而當 python 釋放代理實例時,它將始終「刪除」底層對象。

%newobject 功能旨在向目標語言指示它應該對返回的對象擁有全部權。與具備 ref 功能關聯的類型一塊兒使用時,它還會將 ref 功能中的代碼發送到 C++ 包裝器中。除了上述內容外,還考慮包裝如下工廠函數:

%newobject AFactory;
A *AFactory() {
  return new A();
}

The AFactory function now acts much like a call to the Aconstructor with respect to memory handling:

如今,關於內存處理,AFactory 函數的行爲很像對 A 構造函數的調用:

a = AFactory()    # SWIG ref 'a' due to %newobject (count: 1)
exit              # 'a' is released, SWIG unref 'a' called in the destructor wrapper (count: 0)

6.26 using 聲明與繼承

using declarations are sometimes used to adjust access to members of base classes. For example:

有時使用 using 聲明來調整對基類成員的訪問。例如:

class Foo {
public:
    int blah(int x);
};

class Bar {
public:
    double blah(double x);
};

class FooBar : public Foo, public Bar {
public:
    using Foo::blah;
    using Bar::blah;
    char *blah(const char *x);
};

In this example, the using declarations make different versions of the overloaded blah() method accessible from the derived class. For example:

在這個例子中,using 聲明使派生類能夠訪問重載的 blah() 方法的不一樣版本。例如:

FooBar *f;
f->blah(3);         // Ok. Invokes Foo::blah(int)
f->blah(3.5);       // Ok. Invokes Bar::blah(double)
f->blah("hello");   // Ok. Invokes FooBar::blah(const char *);

SWIG emulates the same functionality when creating wrappers. For example, if you wrap this code in Python, the module works just like you would expect:

SWIG 在建立包裝器時會模擬相同的功能。例如,若是將此代碼包裝在 Python 中,則該模塊的工做方式與你指望的同樣:

>>> import example
>>> f = example.FooBar()
>>> f.blah(3)
>>> f.blah(3.5)
>>> f.blah("hello")

using declarations can also be used to change access when applicable. For example:

能夠的話,也可使用 using 聲明來更改訪問權限。例如:

class Foo {
protected:
    int x;
    int blah(int x);
};

class Bar : public Foo {
public:
    using Foo::x;       // Make x public
    using Foo::blah;    // Make blah public
};

This also works in SWIG ---the exposed declarations will be wrapped normally.

When using declarations are used as shown in these examples, declarations from the base classes are copied into the derived class and wrapped normally. When copied, the declarations retain any properties that might have been attached using %rename, %ignore, or %feature. Thus, if a method is ignored in a base class, it will also be ignored by a using declaration.

Because a using declaration does not provide fine-grained control over the declarations that get imported, it may be difficult to manage such declarations in applications that make heavy use of SWIG customization features. If you can't get using to work correctly, you can always change the interface to the following:

這在 SWIG 中也適用——暴露的聲明將被正常包裝。

如這些示例所示,當使用 using 聲明時,基類中的聲明將被複制到派生類中並正常包裝。複製後,聲明保留 %rename%ignore%feature 附加的任何屬性。所以,若是一個方法在基類中被忽略,它也會被 using 聲明所忽略。

因爲 using 聲明不能對導入的聲明提供細粒度的控制,所以在大量使用 SWIG 自定義功能的應用程序中,可能難以管理此類聲明。若是沒法使 using 正常工做,則能夠始終將接口更改成如下內容:

class FooBar : public Foo, public Bar {
public:
#ifndef SWIG
    using Foo::blah;
    using Bar::blah;
#else
    int blah(int x);         // explicitly tell SWIG about other declarations
    double blah(double x);
#endif

    char *blah(const char *x);
};

Notes:

  • If a derived class redefines a method defined in a base class, then a using declaration won't cause a conflict. For example:

注意

  • 若是派生類從新定義了基類中定義的方法,則 using 聲明不會引發衝突。例如:
class Foo {
public:
  int blah(int );
  double blah(double);
};

class Bar : public Foo {
public:
  using Foo::blah;    // Only imports blah(double);
  int blah(int);
};
  • Resolving ambiguity in overloading may prevent declarations from being imported by using. For example:
  • 解決重載中的歧義可能會阻止經過 using 導入的聲明。例如:
%rename(blah_long) Foo::blah(long);
class Foo {
public:
  int blah(int);
  long blah(long);  // Renamed to blah_long
};

class Bar : public Foo {
public:
  using Foo::blah;     // Only imports blah(int)
  double blah(double x);
};

6.27 嵌套類

If the target language supports the nested classes concept (like Java), the nested C++ classes are wrapped as nested target language proxy classes. (In case of Java - "static" nested classes.) Only public nested classes are wrapped. Otherwise there is little difference between nested and normal classes.

If the target language doesn't support nested classes directly, or the support is not implemented in the language module (like for python currently), then the visible nested classes are moved to the same name space as the containing class (nesting hierarchy is "flattened"). The same behaviour may be turned on for C# and Java by the %feature ("flatnested"); If there is a class with the same name in the outer namespace the inner class (or the global one) may be renamed or ignored:

若是目標語言支持嵌套類概念(例如 Java),則將嵌套的 C++ 類包裝爲嵌套的目標語言代理類。(對於 Java 的「靜態」嵌套類。)僅包裝公有嵌套類。不然,嵌套類和普通類之間幾乎沒有區別。

若是目標語言不直接支持嵌套類,或者未在語言模塊中實現此支持(例如當前針對的是 python),則可見的嵌套類將移至與包含類相同的命名空間(嵌套層次結構被「展平」)。能夠經過 %feature("flatnested") 爲 C# 和 Java 打開相同的行爲。若是外部命名空間中存在一個具備相同名稱的類,則能夠重命名或忽略內部類(或全局類):

%rename (Bar_Foo) Bar::Foo;
class Foo {};
class Bar {
  public:
  class Foo {};
};

If a nested class, within an outer class, has to be used as a template parameter within the outer class, then the template will have to be instantiated with %template before the beginning of the outer class. An example can be found in the Templates section.

Compatibility Note: Prior to SWIG-3.0.0, there was limited nested class support. Nested classes were treated as opaque pointers. However, there was a workaround for nested class support in these older versions requiring the user to replicate the nested class in the global scope, adding in a typedef for the nested class in the global scope and using the "nestedworkaround" feature on the nested class. This resulted in approximately the same behaviour as the "flatnested" feature. With proper nested class support now available in SWIG-3.0.0, this feature has been deprecated and no longer works requiring code changes. If you see the following warning:

若是外部類中的嵌套類必須用做外部類中的模板參數,則必須在外部類開始以前使用 %template 實例化該模板。能夠在模板部分中找到示例。

注意兼容性:在 SWIG-3.0.0 以前,對嵌套類的支持有限。嵌套類被視爲不透明指針。可是,在這些較舊的版本中,存在一種支持嵌套類的解決方法,要求用戶在全局範圍內複製嵌套類,爲全局範圍內的嵌套類添加 typedef,並在嵌套類上使用 nestedworkaround 功能 。這致使與 flatnested 功能大體相同的行爲。如今藉助 SWIG-3.0.0 中提供的嵌套類支持,該功能已被棄用,而且再也不須要更改代碼而起做用。若是看到如下警告:

example.i:8: Warning 126: The nestedworkaround feature is deprecated

consider using the "flatnested" feature discussed above which generates a non-nested proxy class, like the "nestedworkaround" feature did. Alternatively, use the default nested class code generation, which may generate an equivalent to a nested proxy class in the target language, depending on the target language support.

SWIG-1.3.40 and earlier versions did not have the nestedworkaround feature and the generated code resulting from parsing nested classes did not always compile. Nested class warnings could also not be suppressed using %warnfilter.

考慮使用上面討論的 flatnested 功能來生成非嵌套的代理類,就像 nestedworkaround 功能同樣。或者,使用默認的嵌套類代碼生成,這可能會生成與目標語言中的嵌套代理類等效的代碼,具體取決於目標語言的支持。

SWIG-1.3.40 和更早版本不具備 nestedworkaround 功能,而且因爲解析嵌套類而生成的代碼並不老是能夠編譯。使用 %warnfilter 也不能禁止嵌套類警告。

6.28 關於 const 正確性的爭論

A common issue when working with C++ programs is dealing with all possible ways in which the const qualifier (or lack thereof) will break your program, all programs linked against your program, and all programs linked against those programs.

Although SWIG knows how to correctly deal with const in its internal type system and it knows how to generate wrappers that are free of const-related warnings, SWIG does not make any attempt to preserve const-correctness in the target language. Thus, it is possible to pass const qualified objects to non-const methods and functions. For example, consider the following code in C++:

使用 C++ 程序時的一個常見問題是處理 const 限定符(或缺乏 const 限定符),由於程序、與程序連接的全部程序,以及與這些程序連接的全部程序可能所以被破壞。

儘管 SWIG 知道如何在其內部類型系統中正確處理 const,而且知道如何生成沒有 const 相關警告的包裝器,但 SWIG 並何嘗試保留目標語言中的 const 正確性。所以,能夠將合法的常量對象傳遞給很是量方法和函數。例如,請考慮如下 C++ 代碼:

const Object * foo();
void bar(Object *);

...
// C++ code
void blah() {
  bar(foo());         // Error: bar discards const
};

Now, consider the behavior when wrapped into a Python module:

如今,考慮將行爲包裝進 Python 模塊:

>>> bar(foo())         # Okay
>>>

Although this is clearly a violation of the C++ type-system, fixing the problem doesn't seem to be worth the added implementation complexity that would be required to support it in the SWIG run-time type system. There are no plans to change this in future releases (although we'll never rule anything out entirely).

The bottom line is that this particular issue does not appear to be a problem for most SWIG projects. Of course, you might want to consider using another tool if maintaining constness is the most important part of your project.

儘管這顯然違反了 C++ 類型系統,但爲了解決該問題而在 SWIG 運行時類型系統中支持該實現所增長的複雜性看起來彷佛不值得。沒有計劃在未來的版本中對此進行更改(儘管咱們永遠不會徹底排除任何問題)。

最重要的是,對於大多數 SWIG 項目而言,這個特定問題彷佛都不是問題。固然,若是保持常量性是項目中最重要的部分,則可能要考慮使用其餘工具。

6.29 到哪裏得到更多信息

If you're wrapping serious C++ code, you might want to pick up a copy of "The Annotated C++ Reference Manual" by Ellis and Stroustrup. This is the reference document we use to guide a lot of SWIG's C++ support.

若是要包裝嚴謹的 C++ 代碼,則可能須要閱讀 Ellis 和 Stroustrup 撰寫的《The Annotated C++ Reference Manual》。這是咱們用來指導 SWIG 對 C++ 諸多支持的參考文檔。

相關文章
相關標籤/搜索