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.

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

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.

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.

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++ 多繼承包裝到不支持多繼承的目標語言。類似地,當目標語言中不支持重載時,使用重載運算符和重載函數可能是有問題的。

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](http://swig.org/Doc3.0/ SWIG .html# SWIG _default_args). 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](http://swig.org/Doc3.0/ SWIG .html# SWIG _adding_member_functions) 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++ 諸多支持的參考文檔。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM