目錄
- 36 SWIG 與 Python
- 36.1 概覽
- 36.2 預備知識
- 36.2.1 運作 SWIG
- 36.2.2 使用 distutils
- 36.2.3 手動編譯一個動态子產品
- 36.2.4 靜态連結
- 36.2.5 使用你的子產品
- 36.2.6 編譯 C++
- 36.2.7 為 64 位平台編譯
- 36.2.8 在 Windows 下建構 Python 擴充
- 36.3 一個 C/C++ 包裝基礎教程
- 36.3.1 子產品
- 36.3.2 函數
- 36.3.3 全局變量
- 36.3.4 常量與枚舉
- 36.3.5 指針
- 36.3.6 結構體
- 36.3.7 C++ 類
- 36.3.8 C++ 繼承
- 36.3.9 指針、引用、值和數組
- 36.3.10 C++ 重載函數
- 36.3.11 C++ 運算符
- 36.3.12 C++ 命名空間
- 36.3.13 C++ 模闆
- 36.3.14 C++ 智能指針
- 36.3.14.1 shared_ptr 智能指針
- 36.3.14.2 泛型智能指針
- 36.3.15 C++ 引用計數對象
- 36.4 Python 類接口的更多細節
- 36.4.1 代理類
- 36.4.2 内置類型
- 36.4.2.1 限制
- 36.4.2.2 運算符重載與 slot——值得一用!
- 36.4.3 記憶體管理
- 36.4.4 Python 2.2 與經典類
- 36.5 跨語言多态
- 36.5.1 開啟 Director
- 36.5.2 Director 類
- 36.5.3 所有權與對象銷毀
- 36.5.4 異常復原
- 36.5.5 開銷與代碼膨脹
- 36.5.6 類型映射
- 36.5.7 其他雜項
- 36.6 一般定制化特性
- 36.6.1 C/C++ 輔助函數
- 36.6.2 添加而外的 Python 代碼
- 36.6.3 用 %extend 擴充類
- 36.6.4 用 %exception 處理異常
- 36.7 小貼士和常用技術
- 36.7.1 輸入輸出參數
- 36.7.2 簡單指針
- 36.7.3 無界 C 數組
- 36.7.4 處理字元串
- 36.7.5 預設參數
- 36.8 類型映射
- 36.8.1 什麼是類型映射?
- 36.8.2 Python 類型映射
- 36.8.3 類型映射變量
- 36.8.4 有用的 Python 函數
- 36.9 類型映射示例
- 36.9.1 将 Python list 轉換為 char **
- 36.9.2 擴充一個 Python 對象到多參數
- 36.9.3 使用類型映射傳回參數
- 36.9.4 将 Python 元組映射到小數組
- 36.9.5 将序列映射到 C 數組
- 36.9.6 處理指針
- 36.10 Docstring 功能
- 36.10.1 子產品 docstring
- 36.10.2 %feature("autodoc")
- 36.10.2.1 %feature("autodoc", "0")
- 36.10.2.2 %feature("autodoc", "1")
- 36.10.2.3 %feature("autodoc", "2")
- 36.10.2.4 %feature("autodoc", "3")
- 36.10.2.5 %feature("autodoc", "docstring")
- 36.10.3 %feature("docstring")
- 36.11 Python 包
- 36.11.1 設定 Python 包
- 36.11.2 絕對與相對導入
- 36.11.3 增強絕對導入語義
- 36.11.4 從 init.py 導入
- 36.11.5 隐式命名空間包
- 36.11.6 搜尋包裝子產品
- 36.11.6.1 同一個包中的兩個子產品
- 36.11.6.2 切分子產品
- 36.11.6.3 兩個子產品皆為全局
- 36.11.6.4 靜态連結 C 子產品
- 36.12 Python 3 支援
- 36.12.1 函數标注
- 36.12.2 緩沖區接口
- 36.12.3 抽象基類
- 36.12.4 位元組字元串輸出轉換
- 36.12.5 Python 2 Unicode
Caution: This chapter is under repair!
This chapter describes SWIG's support of Python. SWIG is compatible with most recent Python versions including Python 3.0 and Python 2.6, as well as older versions dating back to Python 2.0. For the best results, consider using Python 2.3 or newer.
This chapter covers most SWIG features, but certain low-level details are covered in less depth than in earlier chapters. At the very least, make sure you read the "SWIG Basics" chapter.
本章介紹 SWIG 對 Python 的支援。SWIG 相容包括 Python 3.0 和 Python 2.6 在内的最新 Python 版本,以及可追溯到 Python 2.0 舊版本。為了獲得最佳結果,請考慮使用 Python 2.3 或更高版本。
本章涵蓋了大多數 SWIG 功能,但與以前的章節相比,對某些低級細節的深度介紹較少。請確定你至少已閱讀 SWIG 基礎知識一章。
To build Python extension modules, SWIG uses a layered approach in which parts of the extension module are defined in C and other parts are defined in Python. The C layer contains low-level wrappers whereas Python code is used to define high-level features.
This layered approach recognizes the fact that certain aspects of extension building are better accomplished in each language (instead of trying to do everything in C or C++). Furthermore, by generating code in both languages, you get a lot more flexibility since you can enhance the extension module with support code in either language.
In describing the Python interface, this chapter starts by covering the basics of configuration, compiling, and installing Python modules. Next, the Python interface to common C and C++ programming features is described. Advanced customization features such as typemaps are then described followed by a discussion of low-level implementation details.
為了建構 Python 擴充子產品,SWIG 使用分層方法,其中擴充子產品的某些部分用 C 定義,而其他部分用 Python 定義。C 層包含低級包裝器,而 Python 代碼用于定義進階功能。
這種分層方法基于以下事實:擴充建構的某些方面可以在每種語言中更好地完成(而不是嘗試使用 C 或 C++ 進行所有操作)。此外,通過生成兩種語言的代碼,你将獲得更大的靈活性,因為你可以使用兩種語言的支援代碼來增強擴充子產品。
在描述 Python 接口時,本章首先介紹配置、編譯和安裝 Python 子產品的基礎知識。接下來,描述了一般 C 和 C++ 程式設計功能的 Python 接口。然後介紹進階自定義功能(如類型映射),然後讨論底層實作細節。
Suppose that you defined a SWIG module such as the following:
假設你定義了以下 SWIG 子產品:
/* File: example.i */
%module example
%{
#define SWIG_FILE_WITH_INIT
#include "example.h"
%}
int fact(int n);
The
#define SWIG_FILE_WITH_INIT
line inserts a macro that specifies that the resulting C file should be built as a python extension, inserting the module
init
code. This
.i
file wraps the following simple C file:
行插入一個宏,該宏指定應将生成的 C 檔案建構為 python 擴充,并插入子產品
#define SWIG_FILE_WITH_INIT
代碼。該
init
檔案包裝了以下簡單的 C 檔案:
.i
/* File: example.c */
#include "example.h"
int fact(int n) {
if (n < 0){ /* This should probably return an error, but this is simpler */
return 0;
}
if (n == 0) {
return 1;
}
else {
/* testing for overflow would be a good idea here */
return n * fact(n-1);
}
}
With the header file:
頭檔案:
/* File: example.h */
int fact(int n);
To build a Python module, run SWIG using the
-python
option:
為建構 Python 子產品,運作 SWIG 時要添加 -python
選項:
$ SWIG -python example.i
If building a C++ extension, add the
-c++
如果建構 C++ 擴充,要添加 -c++
$ SWIG -c++ -python example.i
This creates two different files; a C/C++ source file
example_wrap.c
or
example_wrap.cxx
and a Python source file
example.py
. The generated C source file contains the low-level wrappers that need to be compiled and linked with the rest of your C/C++ application to create an extension module. The Python source file contains high-level support code. This is the file that you will import to use the module.
The name of the wrapper file is derived from the name of the input file. For example, if the input file is
example.i
, the name of the wrapper file is
example_wrap.c
. To change this, you can use the
-o
option. The name of the Python file is derived from the module name specified with
%module
. If the module name is
example
, then a file
example.py
is created.
The following sections have further practical examples and details on how you might go about compiling and using the generated files.
這将建立兩個不同的檔案。C/C++ 源檔案或
example_wrap.c
和 Python 源檔案
example_wrap.cxx
example.py
。生成的 C 源檔案包含需要編譯的低級包裝器,并與你的 C/C++ 應用程式的其餘部分連結以建立擴充子產品。Python 源檔案包含進階支援代碼。這是你将導入,并用以使用該子產品的檔案。
包裝檔案的名稱是從輸入檔案的名稱派生的。例如,如果輸入檔案為
,則包裝檔案的名稱為
example.i
。要更改此設定,可以使用
example_wrap.c
選項。Python 檔案的名稱是從
-o
指定的子產品名稱派生的。如果子產品名稱為
%module
,則建立檔案
example
example.py
。
以下各節提供了更多實用的示例,以及有關如何編譯和使用生成檔案的詳細資訊。
36.2.2 使用 distutils
distutils
The preferred approach to building an extension module for python is to compile it with distutils, which comes with all recent versions of python (Distutils Docs).
Distutils takes care of making sure that your extension is built with all the correct flags, headers, etc. for the version of Python it is run with. Distutils will compile your extension into a shared object file or DLL (
.so
on Linux,
.pyd
on Windows, etc). In addition, distutils can handle installing your package into site-packages, if that is desired. A configuration file (conventionally called:
setup.py
) describes the extension (and related python modules). The distutils will then generate all the right compiler directives to build it for you.
Here is a sample
setup.py
file for the above example:
建構 python 擴充子產品的首選方法是使用對其進行編譯,而
distutils
随所有 python 的最新版本一起提供(Distutils Docs)。
distutils
負責確定你的擴充程式針對運作它的 Python 版本使用所有正确的标志、頭檔案等進行建構。
distutils
會将你的擴充編譯為共享對象檔案或 DLL(在 Linux 上為
distutils
,在 Windows 上為
.so
等)。另外,如果需要,
.pyd
可以處理将你的軟體包安裝到站點軟體包中的過程。一個配置檔案(通常稱為
distutils
)描述了擴充(以及相關的 python 子產品)。然後,
setup.py
distutils
将生成所有正确的編譯器指令來為你建構它。
這是上述示例的樣本
檔案:
setup.py
#!/usr/bin/env python
"""
setup.py file for SWIG example
"""
from distutils.core import setup, Extension
example_module = Extension(
'_example',
sources=['example_wrap.c', 'example.c'])
setup(
name = 'example',
version = '0.1',
author = "SWIG Docs",
description = """Simple SWIG example from docs""",
ext_modules = [example_module],
py_modules = ["example"])
In this example, the line:
example_module = Extension(....)
creates an Extension module object, defining the name as
_example
, and using the source code files:
example_wrap.c
, generated by SWIG , and
example.c
, your original c source. The SWIG (and other python extension modules) tradition is for the compiled extension to have the name of the python portion, prefixed by an underscore. If the name of your python module is
example.py
, then the name of the corresponding object file will be
_example.so
.
setup
call then sets up distutils to build your package, defining some meta data, and passing in your extension module object. Once this is saved as
setup.py
, you can build your extension with these commands:
在此示例中,行:建立一個擴充子產品對象,将名稱定義為
example_module = Extension(....)
,并使用由 SWIG 生成的源代碼檔案
_example
和
example_wrap.c
,你的原始 C 源代碼。SWIG(和其他 python 擴充子產品)的傳統是使編譯後的擴充具有 python 部分的名稱,并以下劃線作為字首。如果你的 python 子產品的名稱為
example.c
,則對應的目标檔案的名稱将為
example.py
然後,
_example.so
調用會設定
setup
來建構你的包,定義一些中繼資料,并傳入你的擴充子產品對象。将其儲存為
distutils
後,你可以使用以下指令建構擴充程式:
setup.py
$ SWIG -python example.i
$ python setup.py build_ext --inplace
And a
.so
, or
.pyd
or ... will be created for you. It will build a version that matches the python that you run the command with. Taking apart the command line:
-
-- the version of python you want to build forpython
-
-- the name of your setup script (it can be called anything, butsetup.py
is the tradition)setup.py
-
-- telling distutils to build extensionsbuild_ext
-
-- this tells distutils to put the extension lib in the current dir. Otherwise, it will put it inside a build hierarchy, and you'd have to move it to use it.--inplace
The distutils have many other features, consult the python distutils docs for details.
This same approach works on all platforms if the appropriate compiler is installed. (it can even build extensions to the standard Windows Python using MingGW)
然後将為你建立一個
.so
或其他。它将建構與你運作指令的 python 比對的版本。分析下指令行:
.pyd
—— 你要針對建構的 python 版本
python
——安裝腳本的名稱(可以稱為任何名稱,但
setup.py
是傳統名稱)
setup.py
——告訴
build_ext
建立擴充
distutils
--inplace
将擴充庫 lib 放在目前目錄中。否則,它将被放置在建構層次結構中,你必須将其移動以使用它。
distutils
還有許多其他功能,有關詳細資訊,請查閱 python
distutils
distutils
的文檔。
如果安裝了适當的編譯器,則此方法适用于所有平台。(它甚至可以使用 MingGW 建構對标準 Windows Python 的擴充)
While the preferred approach to building an extension module is to use the distutils, some people like to integrate building extensions with a larger build system, and thus may wish to compile their modules without the distutils. To do this, you need to compile your program using commands like this (shown for Linux):
雖然建構擴充子產品的首選方法是使用,但有些人喜歡将建構擴充與更大的建構系統內建在一起,是以,可能希望在沒有
distutils
的情況下編譯其子產品。為此,你需要使用以下指令編譯程式(針對 Linux,該指令如下所示):
distutils
$ SWIG -python example.i
$ gcc -O2 -fPIC -c example.c
$ gcc -O2 -fPIC -c example_wrap.c -I/usr/local/include/python2.5
$ gcc -shared example.o example_wrap.o -o _example.so
The exact commands for doing this vary from platform to platform. However, SWIG tries to guess the right options when it is installed. Therefore, you may want to start with one of the examples in the
SWIG/Examples/python
directory. If that doesn't work, you will need to read the man-pages for your compiler and linker to get the right set of options. You might also check the SWIG Wiki for additional information.
When linking the module, the name of the output file has to match the name of the module prefixed by an underscore. If the name of your module is
example
, then the name of the corresponding object file should be
_example.so
_examplemodule.so
. The name of the module is specified using the
%module
directive or the
-module
command line option.
Compatibility Note: In SWIG-1.3.13 and earlier releases, module names did not include the leading underscore. This is because modules were normally created as C-only extensions without the extra Python support file (instead, creating Python code was supported as an optional feature). This has been changed in SWIG-1.3.14 and is consistent with other Python extension modules. For example, the
socket
module actually consists of two files;
socket.py
and
_socket.so
. Many other built-in Python modules follow a similar convention.
具體執行的指令因平台而異。但是,SWIG 會在安裝時嘗試猜測正确的選項。是以,你可以想從
SWIG/Examples/python
目錄中的某個示例開始。如果這不起作用,則需要閱讀編譯器和連結器的手冊,以擷取正确的選項集。你也可以檢視 SWIG Wiki 以擷取更多資訊。
連結子產品時,輸出檔案的名稱必須與帶有下劃線字首的子產品的名稱比對。如果子產品的名稱為
,則相應的目标檔案的名稱應為
example
_example.so
。使用
_examplemodule.so
指令或
%module
-module
指令行選項指定子產品的名稱。
相容性說明:在 SWIG-1.3.13 和更早版本中,子產品名稱不包括前導下劃線。這是因為子產品通常僅被建立為 C 擴充,而沒有額外的 Python 支援檔案(相反,建立 Python 代碼作為可選功能)。這已在 SWIG-1.3.14 中更改,并與其他 Python 擴充子產品一緻。例如,
子產品實際上由兩個檔案組成:
socket
socket.py
。許多其他内置的 Python 子產品遵循類似的約定。
_socket.so
An alternative approach to dynamic linking is to rebuild the Python interpreter with your extension module added to it. In the past, this approach was sometimes necessary due to limitations in dynamic loading support on certain machines. However, the situation has improved greatly over the last few years and you should not consider this approach unless there is really no other option.
The usual procedure for adding a new module to Python involves finding the Python source, adding an entry to the
Modules/Setup
file, and rebuilding the interpreter using the Python Makefile. However, newer Python versions have changed the build process. You may need to edit the 'setup.py' file in the Python distribution instead.
In earlier versions of SWIG , the
embed.i
library file could be used to rebuild the interpreter. For example:
動态連結的另一種方法是使用擴充子產品來重建 Python 解釋器。過去,由于某些機器上動态加載支援的限制,有時需要使用此方法。但是,在過去幾年中,這種情況已大大改善,除非你真的沒有其他選擇,否則你不應該考慮采用這種方法。
向 Python 添加新子產品的一般過程包括查找 Python 源代碼,向
檔案添加條目,以及使用 Python Makefile 重建解釋器。但是,較新的 Python 版本改變了建構過程。你可能需要在 Python 發行版中編輯
Modules/Setup
setup.py
檔案。
在早期版本的 SWIG 中,
庫檔案可用于重建解釋器。例如:
embed.i
%module example
%inline %{
extern int fact(int);
extern int mod(int, int);
extern double My_variable;
%}
%include "embed.i" // Include code for a static version of Python
embed.i
library file includes supporting code that contains everything needed to rebuild Python. To rebuild the interpreter, you simply do something like this:
embed.i
庫檔案包含支援代碼,其中包含重建 Python 所需的所有内容。要重建解釋器,你隻需執行以下操作:
$ SWIG -python -lembed.i example.i
$ gcc example.c example_wrap.c \
-Xlinker -export-dynamic \
-DHAVE_CONFIG_H -I/usr/include/python2.7 \
-I/usr/lib/python2.7/config-x86_64-linux-gnu \
-I/usr/lib/python2.7/config \
-L/usr/lib/python2.7/config -lpython2.7 -lm -ldl \
-o mypython
You will need to supply the same libraries that were used to build Python the first time. This may include system libraries such as
-lsocket
,
-lnsl
, and
-lpthread
. Assuming this actually works, the new version of Python should be identical to the default version except that your extension module will be a built-in part of the interpreter.
Comment: In practice, you should probably try to avoid static linking if possible. Some programmers may be inclined to use static linking in the interest of getting better performance. However, the performance gained by static linking tends to be rather minimal in most situations (and quite frankly not worth the extra hassle in the opinion of this author).
Compatibility note: The
embed.i
library file is deprecated and has not been actively maintained for many years. Even though it appears to "work" with Python 2.7, no future support is guaranteed. If using static linking, you might want to rely on a different approach (perhaps using distutils).
你将需要提供與第一次建構 Python 相同的庫。這可能包括系統庫,例如、
-lsocket
-lnsl
-lpthread
。假設這确實有效,則 Python 的新版本應與預設版本相同,不同之處在于擴充子產品将是解釋器的内置部分。
評論:實際上,你應該盡可能避免靜态連結。為了獲得更好的性能,某些程式員可能傾向于使用靜态連結。但是,在大多數情況下,通過靜态連結獲得的性能往往會非常低(坦率地說,依此作者看來,這不值得額外的麻煩)。
相容性說明:不推薦使用
庫檔案,并且多年來沒有對其進行積極維護。即使它似乎可以在 Python 2.7 中使用,也不能保證将來會提供支援。如果使用靜态連結,則可能需要依靠其他方法(也許使用
embed.i
)。
distutils
To use your module, simply use the Python
import
statement. If all goes well, you will be able to run this:
要使用你的子產品,隻需使用 Python 的 import
語句即可。如果一切順利,你将可以運作以下指令:
$ python
>>> import example
>>> example.fact(4)
24
>>>
A common error received by first-time users is the following:
初次使用的使用者收到的常見錯誤如下:
>>> import example
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "example.py", line 2, in ?
import _example
ImportError: No module named _example
If you get this message, it means that you either forgot to compile the wrapper code into an extension module or you didn't give the extension module the right name. Make sure that you compiled the wrappers into a module called
_example.so
. And don't forget the leading underscore (
_
).
Another possible error is the following:
如果收到此消息,則意味着你要麼忘記将包裝器代碼編譯到擴充子產品中,要麼沒有給擴充子產品指定正确的名稱。確定将包裝器編譯到名為的子產品中。并且不要忘了下劃線(
_example.so
另一個可能的錯誤如下:
_
>>> import example
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ImportError: dynamic module does not define init function (init_example)
>>>
This error is almost always caused when a bad name is given to the shared object file. For example, if you created a file
example.so
instead of
_example.so
you would get this error. Alternatively, this error could arise if the name of the module is inconsistent with the module name supplied with the
%module
directive. Double-check the interface to make sure the module name and the shared object filename match. Another possible cause of this error is forgetting to link the SWIG-generated wrapper code with the rest of your application when creating the extension module.
Another common error is something similar to the following:
共享對象檔案的名稱錯誤時,幾乎總是會導緻此錯誤。例如,如果你建立的檔案名為而不是
example.so
,則會出現此錯誤。或者,如果子產品名稱與
_example.so
%module
指令提供的子產品名稱不一緻,則可能會發生此錯誤。仔細檢查接口檔案以確定子產品名稱和動态連結庫檔案名比對。造成此錯誤的另一個可能原因是,在建立擴充子產品時,忘記将 SWIG 生成的包裝器代碼與應用程式的其餘部分連結。
另一個常見錯誤類似于以下内容:
Traceback (most recent call last):
File "example.py", line 3, in ?
import example
ImportError: ./_example.so: undefined symbol: fact
This error usually indicates that you forgot to include some object files or libraries in the linking of the shared library file. Make sure you compile both the SWIG wrapper file and your original program into a shared library file. Make sure you pass all of the required libraries to the linker.
Sometimes unresolved symbols occur because a wrapper has been created for a function that doesn't actually exist in a library. This usually occurs when a header file includes a declaration for a function that was never actually implemented or it was removed from a library without updating the header file. To fix this, you can either edit the SWIG input file to remove the offending declaration or you can use the
%ignore
directive to ignore the declaration.
Finally, suppose that your extension module is linked with another library like this:
此錯誤通常表明你忘記了動态連結庫檔案的連結中包含一些目标檔案或庫。確定将 SWIG 包裝檔案和原始程式都編譯到動态連結庫檔案中。確定将所有必需的庫傳遞給連結器。
有時會出現未解析的符号,因為已為庫中實際上不存在的函數建立了包裝器。當頭檔案包含一個從未真正實作的函數的聲明,或者在不更新頭檔案的情況下将其從庫中删除時,通常會發生這種情況。為了解決這個問題,你可以編輯 SWIG 輸入檔案以删除有問題的聲明,也可以使用
%ignore
指令忽略該聲明。
最後,假設你的擴充子產品已與另一個庫連結,如下所示:
$ gcc -shared example.o example_wrap.o -L/home/beazley/projects/lib -lfoo \
-o _example.so
If the
foo
library is compiled as a shared library, you might encounter the following problem when you try to use your module:
如果将 foo
庫編譯為動态連結庫,則在嘗試使用子產品時可能會遇到以下問題:
>>> import example
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ImportError: libfoo.so: cannot open shared object file: No such file or directory
>>>
This error is generated because the dynamic linker can't locate the
libfoo.so
library. When shared libraries are loaded, the system normally only checks a few standard locations such as
/usr/lib
/usr/local/lib
. To fix this problem, there are several things you can do. First, you can recompile your extension module with extra path information. For example, on Linux you can do this:
生成此錯誤是因為動态連結器無法找到庫。加載動态連結庫後,系統通常僅檢查幾個标準位置,例如
liblib.so
/usr/lib
。要解決此問題,你可以執行幾項操作。首先,你可以使用額外的路徑資訊重新編譯擴充子產品。例如,在 Linux 上,你可以執行以下操作:
/usr/local/lib
$ gcc -shared example.o example_wrap.o -L/home/beazley/projects/lib -lfoo \
-Xlinker -rpath /home/beazley/projects/lib \
-o _example.so
Alternatively, you can set the
LD_LIBRARY_PATH
environment variable to include the directory with your shared libraries. If setting
LD_LIBRARY_PATH
, be aware that setting this variable can introduce a noticeable performance impact on all other applications that you run. To set it only for Python, you might want to do this instead:
另外,你可以設定環境變量以将目錄包含在動态連結庫中。如果設定為
LD_LIBRARY_PATH
,請注意,設定此變量會對運作的所有其他應用程式産生明顯的性能影響。要僅針對 Python 進行設定,你可能需要這樣做:
LD_LIBRARY_PATH
$ env LD_LIBRARY_PATH=/home/beazley/projects/lib python
Finally, you can use a command such as
ldconfig
(Linux) or
crle
(Solaris) to add additional search paths to the default system configuration (this requires root access and you will need to read the man pages).
最後,可以使用諸如(Linux)或
ldconfig
(Solaris)之類的指令将其他搜尋路徑添加到預設系統配置(這需要 root 通路權,并且你需要閱讀手冊)。
crle
Compilation of C++ extensions has traditionally been a tricky problem. Since the Python interpreter is written in C, you need to take steps to make sure C++ is properly initialized and that modules are compiled correctly. This should be a non-issue if you're using distutils, as it takes care of all that for you. The following is included for historical reasons, and in case you need to compile on your own.
On most machines, C++ extension modules should be linked using the C++ compiler. For example:
傳統上,C++ 擴充的編譯是一個棘手的問題。由于 Python 解釋器是用 C 編寫的,是以你需要采取步驟以確定正确地初始化了 C++,并且正确地編譯了子產品。如果你使用的是
distutils
,這應該不是問題,因為它會為你解決所有問題。以下内容出于曆史原因,以防萬一你需要自己進行編譯。
在大多數計算機上,應使用 C++ 編譯器連結 C++ 擴充子產品。例如:
$ SWIG -c++ -python example.i
$ g++ -O2 -fPIC -c example.cxx
$ g++ -O2 -fPIC -c example_wrap.cxx -I/usr/local/include/python2.5
$ g++ -shared example.o example_wrap.o -o _example.so
-fPIC
option tells GCC to generate position-independent code (PIC) which is required for most architectures (it's not vital on x86, but still a good idea as it allows code pages from the library to be shared between processes). Other compilers may need a different option specified instead of
-fPIC
In addition to this, you may need to include additional library files to make it work. For example, if you are using the Sun C++ compiler on Solaris, you often need to add an extra library
-lCrun
like this:
選項告訴 GCC 生成大多數架構所需的位址無關代碼(PIC)(在 x86 上不是至關重要的,但仍然是一個好主意,因為它允許庫中的代碼頁在程序之間共享)。其他編譯器可能需要指定其他選項,而不是
-fPIC
除此之外,你可能需要包括其他庫檔案才能使其正常工作。例如,如果在 Solaris 上使用 Sun C++ 編譯器,則通常需要添加一個額外的庫
-fPIC
,如下所示:
-lCrun
$ SWIG -c++ -python example.i
$ CC -c example.cxx
$ CC -c example_wrap.cxx -I/usr/local/include/python2.5
$ CC -G example.o example_wrap.o -L/opt/SUNWspro/lib -o _example.so -lCrun
Of course, the extra libraries to use are completely non-portable---you will probably need to do some experimentation.
Sometimes people have suggested that it is necessary to relink the Python interpreter using the C++ compiler to make C++ extension modules work. In the experience of this author, this has never actually appeared to be necessary. Relinking the interpreter with C++ really only includes the special run-time libraries described above---as long as you link your extension modules with these libraries, it should not be necessary to rebuild Python.
If you aren't entirely sure about the linking of a C++ extension, you might look at an existing C++ program. On many Unix machines, the
ldd
command will list library dependencies. This should give you some clues about what you might have to include when you link your extension module. For example:
當然,要使用的額外庫完全是不可移植的——你可能需要做一些實驗。
有時人們建議必須使用 C++ 編譯器重新連結 Python 解釋器,以使 C++ 擴充子產品正常工作。根據作者的經驗,這實際上從來沒有必要。使用 C++ 重新連結解釋器實際上僅包含上述特殊的運作時庫——隻要将擴充子產品與這些庫連結,就不必重建 Python。
如果你不确定 C++ 擴充的連結,則可以檢視現有的 C++ 程式。在許多 Unix 機器上,
指令将列出庫依賴關系。這應該為你提供一些有關連結擴充子產品時可能必須包含的内容的線索。例如:
ldd
$ ldd SWIG
libstdc++-libc6.1-1.so.2 => /usr/lib/libstdc++-libc6.1-1.so.2 (0x40019000)
libm.so.6 => /lib/libm.so.6 (0x4005b000)
libc.so.6 => /lib/libc.so.6 (0x40077000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
As a final complication, a major weakness of C++ is that it does not define any sort of standard for binary linking of libraries. This means that C++ code compiled by different compilers will not link together properly as libraries nor is the memory layout of classes and data structures implemented in any kind of portable manner. In a monolithic C++ program, this problem may be unnoticed. However, in Python, it is possible for different extension modules to be compiled with different C++ compilers. As long as these modules are self-contained, this probably won't matter. However, if these modules start sharing data, you will need to take steps to avoid segmentation faults and other erratic program behavior. If working with lots of software components, you might want to investigate using a more formal standard such as COM.
最後的麻煩是,C++ 的主要缺點是它沒有為庫的二進制連結定義任何類型的标準。這意味着由不同的編譯器編譯的 C++ 代碼将無法正确連結為庫,也無法以任何可移植的方式實作類和資料結構體的記憶體布局。在單一整體的 C++ 程式中,此問題可能沒有引起注意。但是,在 Python 中,可以使用不同的 C++ 編譯器來編譯不同的擴充子產品。隻要這些子產品是獨立的,這可能就無關緊要。但是,如果這些子產品開始共享資料,則将需要采取步驟來避免分段錯誤和其他不穩定的程式行為。如果使用大量軟體元件,則可能需要使用更正式的标準(例如 COM)進行調查。
On platforms that support 64-bit applications (Solaris, Irix, etc.), special care is required when building extension modules. On these machines, 64-bit applications are compiled and linked using a different set of compiler/linker options. In addition, it is not generally possible to mix 32-bit and 64-bit code together in the same application.
To utilize 64-bits, the Python executable will need to be recompiled as a 64-bit application. In addition, all libraries, wrapper code, and every other part of your application will need to be compiled for 64-bits. If you plan to use other third-party extension modules, they will also have to be recompiled as 64-bit extensions.
If you are wrapping commercial software for which you have no source code, you will be forced to use the same linking standard as used by that software. This may prevent the use of 64-bit extensions. It may also introduce problems on platforms that support more than one linking standard (e.g., -o32 and -n32 on Irix).
On the Linux x86_64 platform (Opteron or EM64T), besides of the required compiler option -fPIC discussed above, you will need to be careful about the libraries you link with or the library path you use. In general, a Linux distribution will have two set of libraries, one for native x86_64 programs (under /usr/lib64), and another for 32 bits compatibility (under /usr/lib). Also, the compiler options -m32 and -m64 allow you to choose the desired binary format for your python extension.
在支援 64 位應用程式的平台(Solaris、Irix 等)上,建構擴充子產品時需要特别注意。在這些計算機上,使用一組不同的編譯器或連結器選項來編譯和連結 64 位應用程式。此外,通常不可能在同一應用程式中将 32 位和 64 位代碼混合在一起。
要利用 64 位,需要将 Python 可執行檔案重新編譯為 64 位應用程式。此外,所有庫、包裝代碼以及應用程式的所有其他部分都需要編譯為 64 位。如果你打算使用其他第三方擴充子產品,則它們也必須重新編譯為 64 位擴充。
如果要包裝沒有源代碼的商業軟體,則将被迫使用與該軟體相同的連結标準。這可能會阻止使用 64 位擴充名。它還可能在支援多個連結标準的平台(例如 Irix 上的
-o32
-n32
)上引入問題。
在 Linux x86_64 平台(Opteron 或 EM64T)上,除了上面讨論的必需的編譯器選項
外,你還需要注意與之連結的庫或使用的庫路徑。通常,Linux 發行版将具有兩組庫,一組用于本地 x86_64 程式(在
-fPIC
下),另一組用于 32 位相容性(在
/usr/lib64
下)。另外,編譯器選項
/usr/lib
-m32
允許你為 python 擴充選擇所需的二進制格式。
-m64
Building a SWIG extension to Python under Windows is roughly similar to the process used with Unix. Using the distutils, it is essentially identical. If you have the same version of the MS compiler that Python was built with (the python2.4 and python2.5 distributed by python.org are built with Visual Studio 2003), the standard
python setup.py build
should just work.
As of python2.5, the distutils support building extensions with MingGW out of the box. Following the instruction here: Building Python extensions for Windows with only free tools should get you started.
If you need to build it on your own, the following notes are provided:
You will need to create a DLL that can be loaded into the interpreter. This section briefly describes the use of SWIG with Microsoft Visual C++. As a starting point, many of SWIG's examples include project files (.dsp files) for Visual C++ 6. These can be opened by more recent versions of Visual Studio. You might want to take a quick look at these examples in addition to reading this section.
In Developer Studio, SWIG should be invoked as a custom build option. This is usually done as follows:
- Open up a new workspace and use the AppWizard to select a DLL project.
- Add both the SWIG interface file (the .i file), any supporting C files, and the name of the wrapper file that will be created by SWIG (ie.
). Note : If using C++, choose a different suffix for the wrapper file such asexample_wrap.c
. Don't worry if the wrapper file doesn't exist yet--Developer Studio keeps a reference to it.example_wrap.cxx
- Select the SWIG interface file and go to the settings menu. Under settings, select the "Custom Build" option.
- Enter "SWIG" in the description field.
- Enter
in the "Build command(s) field"SWIG -python -o $(ProjDir)\$(InputName)_wrap.c $(InputPath)
-
in the "Output files(s) field".$(ProjDir)\$(InputName)_wrap.c
- Next, select the settings for the entire project and go to "C++:Preprocessor". Add the include directories for your Python installation under "Additional include directories".
- Define the symbol
under preprocessor options.__WIN32__
- Finally, select the settings for the entire project and go to "Link Options". Add the Python library file to your link libraries. For example "python21.lib". Also, set the name of the output file to match the name of your Python module, ie. _example.pyd - Note that _example.dll also worked with Python-2.4 and earlier.
- Build your project.
If all went well, SWIG will be automatically invoked whenever you build your project. Any changes made to the interface file will result in SWIG being automatically executed to produce a new version of the wrapper file.
To run your new Python extension, simply run Python and use the
import
command as normal. For example :
在 Windows 下為 Python 建構 SWIG 擴充程式與 Unix 大緻類似。使用,基本上是相同的。如果你使用與建構 Python 相同的 MS 編譯器版本(由 python.org 分發的 python2.4 和 python2.5 是由 Visual Studio 2003 建構的),則标準的
distutils
python setup.py build
應該可以正常工作。
從 python2.5 開始,
distutils
支援開箱即用的 MingGW 建構擴充。請按照此處的說明進行操作:僅使用免費工具建構 Windows 的 Python 擴充可以幫助你入門。
如果需要自己建構,請提供以下注意事項:
你将需要建立一個可以加載到解釋器中的 DLL。本節簡要介紹了将 SWIG 與 Microsoft Visual C++ 一起使用。首先,SWIG 的許多示例都包含用于 Visual C++ 6 的項目檔案(
.dsp
檔案)。可以使用最新版本的 Visual Studio 打開這些檔案。除了閱讀本節之外,你可能還想快速看一下這些示例。
在 Developer Studio 中,應将 SWIG 作為自定義生成選項調用。通常按以下步驟進行:
- 打開一個新的工作區,并使用 AppWizard 選擇一個 DLL 項目。
- 添加 SWIG 接口檔案(
檔案),所有支援的 C 檔案以及 SWIG 将建立的包裝檔案的名稱(即
.i
)。注意:如果使用 C++,請為包裝檔案選擇其他字尾,例如
example_wrap.c
。不用擔心包裝檔案是否不存在,Developer Studio 會保留對其的引用。
example_wrap.cxx
- 選擇 SWIG 接口檔案,然後轉到設定菜單。在設定下,選擇
選項。
自定義版本
- 在說明字段中輸入
SWIG
- 在“建構指令”字段中輸入
SWIG -python -o $(ProjDir)\ $(InputName)_wrap.c $(InputPath)
- 在“輸出檔案”字段中輸入
$(ProjDir)\ $(InputName)_wrap.c
- 接下來,選擇整個項目的設定,然後轉到
。在“其他包含目錄”下為你的 Python 安裝添加包含目錄。
C++:Preprocessor
- 在預處理器選項下定義符号
__WIN32__
- 最後,選擇整個項目的設定,然後轉到“連結選項”。将 Python 庫檔案添加到連結庫。例如
。另外,設定輸出檔案的名稱以比對你的 Python 子產品的名稱。
python21.lib
——注意
_example.pyd
也可用于 Python2.4 和更早版本。
_example.dll
- 建立你的項目。
如果一切順利,則在你建構項目時将自動調用 SWIG 。對接口檔案所做的任何更改都将導緻 SWIG 自動執行以産生新版本的包裝檔案。
要運作新的 Python 擴充,隻需運作 Python 并正常使用
指令即可。例如:
import
$ python
>>> import example
>>> print example.fact(4)
24
>>>
If you get an
ImportError
exception when importing the module, you may have forgotten to include additional library files when you built your module. If you get an access violation or some kind of general protection fault immediately upon import, you have a more serious problem. This is often caused by linking your extension module against the wrong set of Win32 debug or thread libraries. You will have to fiddle around with the build options of project to try and track this down.
A 'Debug' build of the wrappers requires a debug build of the Python interpreter. This normally requires building the Python interpreter from source, which is not a job for the feint-hearted. Alternatively you can use the 'Release' build of the Python interpreter with a 'Debug' build of your wrappers by defining the
SWIG_PYTHON_INTERPRETER_NO_DEBUG
symbol under the preprocessor options. Or you can ensure this macro is defined at the beginning of the wrapper code using the following in your interface file, where
_MSC_VER
ensures it is only used by the Visual Studio compiler:
如果在導入子產品時收到
ImportError
異常,則可能在構模組化塊時忘記了包含其他庫檔案。如果在導入時立即遇到通路沖突或某種正常保護錯誤,那麼你會遇到更嚴重的問題。這通常是由于将擴充子產品連結到錯誤的 Win32 調試或線程庫集引起的。你将不得不弄亂項目的建構選項,以嘗試對其進行跟蹤。
包裝程式的調試版本需要 Python 解釋器的調試版本。這通常需要從源代碼建構 Python 解釋器,這對膽小的人來說不是一件容易的事。或者,你可以通過在預處理器選項下定義
符号來将 Python 解釋器的釋出版本與包裝的調試版本一起使用。或者,你可以使用接口檔案中的以下指令確定在包裝代碼的開頭定義了此宏,其中
SWIG_PYTHON_INTERPRETER_NO_DEBUG
確定僅由 Visual Studio 編譯器使用:
_MSC_VER
%begin %{
#ifdef_MSC_VER
#define SWIG_PYTHON_INTERPRETER_NO_DEBUG
#endif
%}
Some users have reported success in building extension modules using Cygwin and other compilers. However, the problem of building usable DLLs with these compilers tends to be rather problematic. For the latest information, you may want to consult the SWIG Wiki.
一些使用者報告說成功使用 Cygwin 和其他編譯器建構擴充子產品。但是,使用這些編譯器建構可用的 DLL 的問題往往很成問題。有關最新資訊,你可能需要查閱 SWIG Wiki。
By default, SWIG tries to build a very natural Python interface to your C/C++ code. Functions are wrapped as functions, classes are wrapped as classes, and so forth. This section briefly covers the essential aspects of this wrapping.
預設情況下,SWIG 會嘗試為你的 C/C++ 代碼建構一個非常自然的 Python 接口。函數包裝為函數,類包裝為類,依此類推。本節簡要介紹了此包裝的基本方面。
The SWIG
%module
directive specifies the name of the Python module. If you specify
%module example
, then everything is wrapped into a Python
example
module. Underneath the covers, this module consists of a Python source file
example.py
and a low-level extension module
_example.so
. When choosing a module name, make sure you don't use the same name as a built-in Python command or standard module name.
SWIG 指令指定 Python 子產品的名稱。如果你指定了
%module
,那麼所有的東西都會被包裝到一個叫
%module example
的 Python 子產品中。在幕後,這個子產品由一個 Python 源檔案
example
和一個低級擴充子產品
example.py
組成。選擇子產品名稱時,請確定不要使用與内置 Python 指令或标準子產品相同的名稱。
_example.so
Global functions are wrapped as new Python built-in functions. For example,
全局函數被包裝為一個新的 Python 内置函數。例如,
%module example
int fact(int n);
creates a built-in function
example.fact(n)
that works exactly like you think it does:
建立一個内置函數 example.fact(n)
,工作方式和你想的一樣:
>>> import example
>>> print example.fact(4)
24
>>>
C/C++ global variables are fully supported by SWIG . However, the underlying mechanism is somewhat different than you might expect due to the way that Python assignment works. When you type the following in Python
SWIG 完全支援 C/C++ 全局變量。但是,由于 Python 配置設定的工作方式,其底層機制與你預期的有所不同。當你在 Python 中鍵入以下内容時
a = 3.4
"a" becomes a name for an object containing the value 3.4. If you later type
a
是一個包含值 3.4 的對象的名字。如果你接着鍵入
b = a
then "a" and "b" are both names for the object containing the value 3.4. Thus, there is only one object containing 3.4 and "a" and "b" are both names that refer to it. This is quite different than C where a variable name refers to a memory location in which a value is stored (and assignment copies data into that location). Because of this, there is no direct way to map variable assignment in C to variable assignment in Python.
To provide access to C global variables, SWIG creates a special object called
cvar
' that is added to each SWIG generated module. Global variables are then accessed as attributes of this object. For example, consider this interface
那麼
a
都是包含值 3.4 的對象的名稱。是以,隻有一個對象包含 3.4,并且
b
a
b
都是引用它的名稱。這與 C 完全不同,C 的變量名是指存儲值的存儲位置(指派将資料複制到該位置)。是以,沒有直接的方法可以将 C 中的變量配置設定映射到 Python 中的變量配置設定。
為了提供對 C 全局變量的通路,SWIG 建立了一個名為
的特殊對象,該對象被添加到每個 SWIG 生成的子產品中。然後通路全局變量作為該對象的屬性。例如,考慮以下接口
cvar
//SWIG interface file with global variables
%module example
...
%inline %{
extern int My_variable;
extern double density;
%}
...
Now look at the Python interface:
現在看看 Python 接口:
>>> import example
>>> # Print out value of a C global variable
>>> print example.cvar.My_variable
4
>>> # Set the value of a C global variable
>>> example.cvar.density = 0.8442
>>> # Use in a math operation
>>> example.cvar.density = example.cvar.density*1.10
If you make an error in variable assignment, you will receive an error message. For example:
如果你在變量聲明中犯了錯誤,你會收到一條錯誤資訊。例如:
>>> example.cvar.density = "Hello"
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: C variable 'density (double)'
>>>
If a variable is declared as
const
, it is wrapped as a read-only variable. Attempts to modify its value will result in an error.
To make ordinary variables read-only, you can use the
%immutable
directive. For example:
如果變量聲明為
const
,則将其包裝為隻讀變量。嘗試修改其值将導緻錯誤。
要使普通變量為隻讀,可以使用
指令。例如:
%immutable
%{
extern char *path;
%}
%immutable;
extern char *path;
%mutable;
%immutable
directive stays in effect until it is explicitly disabled or cleared using
%mutable
. See the Creating read-only variables section for further details.
If you just want to make a specific variable immutable, supply a declaration name. For example:
指令将一直有效,直到使用
%immutable
%mutable
将其明确禁用或清除為止。有關更多詳細資訊,請參見建立隻讀變量部分。
如果隻想使特定變量不可變,請提供聲明名稱。例如:
%{
extern char *path;
%}
%immutable path;
...
extern char *path; // Read-only (due to %immutable)
If you would like to access variables using a name other than
cvar
, it can be changed using the
-globals
option :
如果你想使用以外的名稱通路變量,則可以使用
cvar
選項進行更改:
-globals
$ SWIG -python -globals myvar example.i
Some care is in order when importing multiple SWIG modules. If you use the
from import *
style of importing, you will get a name clash on the variable
cvar
' and you will only be able to access global variables from the last module loaded. To prevent this, you might consider renaming
cvar
or making it private to the module by giving it a name that starts with a leading underscore. SWIG does not create
cvar
if there are no global variables in a module.
導入多個 SWIG 子產品時需要特别注意。如果使用導入方式,則變量
from import *
會發生名稱沖突,并且隻能從最後加載的子產品通路全局變量。為了防止這種情況,你可以考慮重命名
cvar
,或通過給子產品起一個以下劃線開頭的名稱來使其對子產品私有。如果子產品中沒有全局變量,SWIG 不會建立
cvar
cvar
C/C++ constants are installed as Python objects containing the appropriate value. To create a constant, use
#define
enum
, or the
%constant
C/C++ 常量作為包含适當值的 Python 對象安裝。要建立一個常量,請使用,
#define
enum
%constant
#define PI 3.14159
#define VERSION "1.0"
enum Beverage { ALE, LAGER, STOUT, PILSNER };
%constant int FOO = 42;
%constant const char *path = "/usr/local";
For enums, make sure that the definition of the enumeration actually appears in a header file or in the wrapper file somehow---if you just stick an enum in a SWIG interface without also telling the C compiler about it, the wrapper code won't compile.
Note: declarations declared as
const
are wrapped as read-only variables and will be accessed using the
cvar
object described in the previous section. They are not wrapped as constants. For further discussion about this, see the SWIG Basics chapter.
Constants are not guaranteed to remain constant in Python---the name of the constant could be accidentally reassigned to refer to some other object. Unfortunately, there is no easy way for SWIG to generate code that prevents this. You will just have to be careful.
對于枚舉,請確定枚舉的定義實際上以某種方式出現在頭檔案或包裝器檔案中,如果你隻是将枚舉粘貼在 SWIG 接口檔案中而不告知 C 編譯器,則包裝器代碼将不會編譯。
注意:聲明為
的變量被包裝為隻讀變量,将使用上一節中描述的
const
cvar
對象進行通路。它們沒有包裝為常量。有關此問題的更多讨論,請參見 SWIG 基礎知識一章。
不能保證在 Python 中常量會保持不變——常量的名稱可能會被意外重配置設定以引用其他對象。不幸的是,SWIG 沒有簡單的方法來生成防止這種情況的代碼。你隻需要小心。
C/C++ pointers are fully supported by SWIG . Furthermore, SWIG has no problem working with incomplete type information. Here is a rather simple interface:
SWIG 完全支援 C/C++ 指針。此外,SWIG 在處理不完整的類型資訊時沒有問題。這裡有一個相當簡單的接口:
%module example
FILE *fopen(const char *filename, const char *mode);
int fputs(const char *, FILE *);
int fclose(FILE *);
When wrapped, you will be able to use the functions in a natural way from Python. For example:
打包後,你将能夠從 Python 自然地使用這些函數。例如:
>>> import example
>>> f = example.fopen("junk", "w")
>>> example.fputs("Hello World\n", f)
>>> example.fclose(f)
If this makes you uneasy, rest assured that there is no deep magic involved. Underneath the covers, pointers to C/C++ objects are simply represented as opaque values using an especial python container object:
如果這讓你感到不安,請放心,其中不涉及任何深奧的魔法。在幕後,使用特殊的 Python 容器對象将 C/C++ 對象的指針簡單地表示為不透明值:
>>> print f
< SWIG Object of type 'FILE *' at 0xb7d6f470>
This pointer value can be freely passed around to different C functions that expect to receive an object of type
FILE *
. The only thing you can't do is dereference the pointer from Python. Of course, that isn't much of a concern in this example.
In older versions of SWIG (1.3.22 or older), pointers were represented using a plain string object. If you have an old package that still requires that representation, or you just feel nostalgic, you can always retrieve it by casting the pointer object to a string:
這個指針值可以自由地傳遞給不同的 C 函數,而這些函數希望接受
FILE *
類型的對象。你唯一不能做的就是從 Python 解引用指針。當然,在此示例中,這并不是什麼大問題。
在較早版本的 SWIG(1.3.22 或更早版本)中,指針使用純字元串對象表示。如果你有一個舊的程式包仍然需要該表示形式,或者隻是懷舊,可以随時通過将指針對象轉換為字元串來檢索它:
>>> print str(f)
_c0671108_p_FILE
Also, if you need to pass the raw pointer value to some external python library, you can do it by casting the pointer object to an integer:
另外,如果你需要将原始指針值傳遞給某個外部 python 庫,則可以通過将指針對象轉換為整數來實作:
>>> print int(f)
135833352
However, the inverse operation is not possible, i.e., you can't build a SWIG pointer object from a raw integer value.
Note also that the '0' or NULL pointer is always represented by
None
, no matter what type SWIG is addressing. In the previous example, you can call:
但是,反向操作是不可能的,你不能從原始整數值建構 SWIG 指針對象。
還要注意,無論 SWIG 尋址哪種類型,
或 NULL 指針始終由表示。在上一個示例中,你可以調用:
None
>>> example.fclose(None)
and that will be equivalent to the following, but not really useful, C code:
這等價于下面,但并非真的有用,C 代碼:
FILE *f = NULL;
fclose(f);
As much as you might be inclined to modify a pointer value directly from Python, don't. The hexadecimal encoding is not necessarily the same as the logical memory address of the underlying object. Instead it is the raw byte encoding of the pointer value. The encoding will vary depending on the native byte-ordering of the platform (i.e., big-endian vs. little-endian). Similarly, don't try to manually cast a pointer to a new type by simply replacing the type-string. This may not work like you expect, it is particularly dangerous when casting C++ objects. If you need to cast a pointer or change its value, consider writing some helper functions instead. For example:
盡管你可能傾向于直接從 Python 修改指針值,但不要這樣做。十六進制編碼不一定與底層對象的邏輯記憶體位址相同。相反,它是指針值的原始位元組編碼。編碼會根據平台的原始位元組順序而有所不同(即 big-endian 與 little-endian)。同樣,不要嘗試通過簡單地替換類型字元串來手動将指針轉換為新類型。這可能無法按預期工作,在轉換 C++ 對象時尤其危險。如果需要強制轉換指針或更改其值,請考慮編寫一些輔助函數。例如:
%inline %{
/* C-style cast */
Bar *FooToBar(Foo *f) {
return (Bar *) f;
}
/* C++-style cast */
Foo *BarToFoo(Bar *b) {
return dynamic_cast<Foo*>(b);
}
Foo *IncrFoo(Foo *f, int i) {
return f+i;
}
%}
Also, if working with C++, you should always try to use the new C++ style casts. For example, in the above code, the C-style cast may return a bogus result whereas as the C++-style cast will return
None
if the conversion can't be performed.
另外,如果使用 C++,則應始終嘗試使用新的 C++ 樣式強制轉換。例如,在上面的代碼中,C 樣式轉換可能會傳回假結果,而 C++ 樣式轉換會在無法執行轉換的情況下傳回 None
If you wrap a C structure, it is wrapped by a Python class. This provides a very natural interface. For example,
如果包裝 C 結構體,則由 Python 類包裝。這提供了非常自然的接口。例如,
struct Vector {
double x, y, z;
};
is used as follows:
将以如下方式是用:
>>> v = example.Vector()
>>> v.x = 3.5
>>> v.y = 7.2
>>> print v.x, v.y, v.z
7.8 -4.5 0.0
>>>
Similar access is provided for unions and the data members of C++ classes.
If you print out the value of
v
in the above example, you will see something like this:
為聯合體和 C++ 類的資料成員提供了類似的通路。
如果在上面的示例中列印出
的值,你将看到類似以下内容:
v
>>> print v
<C Vector instance at _18e31408_p_Vector>
This object is actually a Python instance that has been wrapped around a pointer to the low-level C structure. This instance doesn't actually do anything--it just serves as a proxy. The pointer to the C object can be found in the
.this
attribute. For example:
該對象實際上是一個 Python 執行個體,該執行個體被包裝在指向低級 C 結構體的指針周圍。該執行個體實際上不執行任何操作,隻是充當代理。指向 C 對象的指針可以在 .this
屬性中找到。例如:
>>> print v.this
_18e31408_p_Vector
>>>
Further details about the Python proxy class are covered a little later.
const
members of a structure are read-only. Data members can also be forced to be read-only using the
%immutable
有關 Python 代理類的更多詳細資訊将在稍後介紹。
結構體的
成員是隻讀的。還可以使用
const
指令将資料成員強制為隻讀。例如:
%immutable
struct Foo {
...
%immutable;
int x; /* Read-only members */
char *name;
%mutable;
...
};
When
char *
members of a structure are wrapped, the contents are assumed to be dynamically allocated using
malloc
new
(depending on whether or not SWIG is run with the -c++ option). When the structure member is set, the old contents will be released and a new value created. If this is not the behavior you want, you will have to use a typemap (described later).
If a structure contains arrays, access to those arrays is managed through pointers. For example, consider this:
當包裝結構體的成員時,假定内容是使用
char *
malloc
動态配置設定的(取決于 SWIG 是否使用
new
-c++
選項運作)。設定結構體成員後,将釋放舊内容并建立新值。如果這不是你想要的行為,則必須使用一個類型映射(稍後描述)。
如果結構體包含數組,則通過指針管理對這些數組的通路。例如,考慮以下:
struct Bar {
int x[16];
};
If accessed in Python, you will see behavior like this:
如果使用 Python 通路,你将看到以下行為:
>>> b = example.Bar()
>>> print b.x
_801861a4_p_int
>>>
This pointer can be passed around to functions that expect to receive an
int *
(just like C). You can also set the value of an array member using another pointer. For example:
該指針可以傳遞給期望接受 int *
的函數(就像 C 一樣)。你還可以使用另一個指針設定數組成員的值。例如:
>>> c = example.Bar()
>>> c.x = b.x # Copy contents of b.x to c.x
For array assignment, SWIG copies the entire contents of the array starting with the data pointed to by
b.x
. In this example, 16 integers would be copied. Like C, SWIG makes no assumptions about bounds checking---if you pass a bad pointer, you may get a segmentation fault or access violation.
When a member of a structure is itself a structure, it is handled as a pointer. For example, suppose you have two structures like this:
對于數組配置設定,SWIG 從
b.x
指向的資料開始複制數組的全部内容。在此示例中,将複制 16 個整數。與 C 一樣,SWIG 也不做邊界檢查的假設,如果傳遞錯誤的指針,則可能會遇到分段錯誤或通路沖突。
當結構體的成員本身是結構體時,它将作為指針處理。例如,假設你有兩個這樣的結構體:
struct Foo {
int a;
};
struct Bar {
Foo f;
};
Now, suppose that you access the
f
attribute of
Bar
現在,假設你通路的
Bar
屬性:
f
>>> b = Bar()
>>> x = b.f
In this case,
x
is a pointer that points to the
Foo
that is inside
b
. This is the same value as generated by this C code:
在這種情況下,是指向
x
内部的
b
的指針。此值與此 C 代碼生成的值相同:
Foo
Bar b;
Foo *x = &b->f; /* Points inside b */
Because the pointer points inside the structure, you can modify the contents and everything works just like you would expect. For example:
因為指針指向結構體内部,是以你可以修改内容,一切都可以按你期望的方式進行。例如:
>>> b = Bar()
>>> b.f.a = 3 # Modify attribute of structure member
>>> x = b.f
>>> x.a = 3 # Modifies the same structure
C++ classes are wrapped by Python classes as well. For example, if you have this class,
C++ 類也由 Python 類包裝。例如,如果你有這樣的類,
class List {
public:
List();
~List();
int search(char *item);
void insert(char *item);
void remove(char *item);
char *get(int n);
int length;
};
you can use it in Python like this:
在 Python 中你可以這樣是用:
>>> l = example.List()
>>> l.insert("Ale")
>>> l.insert("Stout")
>>> l.insert("Lager")
>>> l.get(1)
'Stout'
>>> print l.length
3
>>>
Class data members are accessed in the same manner as C structures.
Static class members present a special problem for Python. Prior to Python-2.2, Python classes had no support for static methods and no version of Python supports static member variables in a manner that SWIG can utilize. Therefore, SWIG generates wrappers that try to work around some of these issues. To illustrate, suppose you have a class like this:
類資料成員的通路方式與 C 結構體相同。
靜态類成員為 Python 帶來了一個特殊問題。在 Python-2.2 之前,Python 類不支援靜态方法,并且沒有任何版本的 Python 支援 SWIG 可以利用的靜态成員變量。是以,SWIG 會生成包裝程式,以嘗試解決其中一些問題。為了說明這一點,假設你有一個像這樣的類:
class Spam {
public:
static void foo();
static int bar;
};
In Python, the static member can be access in three different ways:
在 Python 中,靜态方法可以以三種不同的方式通路:
>>> example.Spam_foo() # Spam::foo()
>>> s = example.Spam()
>>> s.foo() # Spam::foo() via an instance
>>> example.Spam.foo() # Spam::foo(). Python-2.2 only
The first two methods of access are supported in all versions of Python. The last technique is only available in Python-2.2 and later versions.
Static member variables are currently accessed as global variables. This means, they are accessed through
cvar
所有版本的 Python 都支援前兩種通路方法。最後一種技術僅在 Python-2.2 和更高版本中可用。
靜态成員變量目前作為全局變量通路。這意味着,可以通過
來通路它們,如下所示:
cvar
>>> print example.cvar.Spam_bar
7
SWIG is fully aware of issues related to C++ inheritance. Therefore, if you have classes like this
SWIG 完全了解與 C++ 繼承有關的問題。是以,如果你有這樣的類
class Foo {
...
};
class Bar : public Foo {
...
};
those classes are wrapped into a hierarchy of Python classes that reflect the same inheritance structure. All of the usual Python utility functions work normally:
這些類被包裝成可反映相同繼承結構的 Python 類層次結構。所有常用的 Python 實用程式功能均可正常運作:
>>> b = Bar()
>>> instance(b, Foo)
1
>>> issubclass(Bar, Foo)
1
>>> issubclass(Foo, Bar)
0
Furthermore, if you have functions like this
更進一步,如果你有這樣的函數:
void spam(Foo *f);
then the function
spam()
accepts
Foo *
or a pointer to any class derived from
Foo
It is safe to use multiple inheritance with SWIG .
然後函數接受
spam()
或指向從
Foo *
Foo
的任何派生類的指針。
在 SWIG 中使用多重繼承是安全的。
In C++, there are many different ways a function might receive and manipulate objects. For example:
在 C++ 中,函數可以通過許多不同的方式接受和操作對象。例如:
void spam1(Foo *x); // Pass by pointer
void spam2(Foo &x); // Pass by reference
void spam3(const Foo &x);// Pass by const reference
void spam4(Foo x); // Pass by value
void spam5(Foo x[]); // Array of objects
In Python, there is no detailed distinction like this--specifically, there are only "objects". There are no pointers, references, arrays, and so forth. Because of this, SWIG unifies all of these types together in the wrapper code. For instance, if you actually had the above functions, it is perfectly legal to do this:
在 Python 中,沒有這樣的詳細區分。具體來說,隻有對象。沒有指針,引用,數組等。是以,SWIG 在包裝代碼中将所有這些類型統一在一起。例如,如果你實際上具有上述函數,則這樣做是完全合法的:
>>> f = Foo() # Create a Foo
>>> spam1(f) # Ok. Pointer
>>> spam2(f) # Ok. Reference
>>> spam3(f) # Ok. Const reference
>>> spam4(f) # Ok. Value.
>>> spam5(f) # Ok. Array (1 element)
Similar behavior occurs for return values. For example, if you had functions like this,
類似的行為出現在傳回值上。例如,如果你的函數像這樣,
Foo *spam6();
Foo &spam7();
Foo spam8();
const Foo &spam9();
then all three functions will return a pointer to some
Foo
object. Since the third function (spam8) returns a value, newly allocated memory is used to hold the result and a pointer is returned (Python will release this memory when the return value is garbage collected). The fourth case (spam9) which returns a const reference, in most of the cases will be treated as a returning value, and it will follow the same allocation/deallocation process.
那麼這三個函數都會傳回一個指向對象的指針。由于第三個函數(
Foo
)傳回一個值,是以将使用新配置設定的記憶體來儲存結果并傳回一個指針(當對傳回值進行垃圾回收時,Python 将釋放該記憶體)。傳回常量引用的第四種情況(
spam8
)在大多數情況下将被視為傳回值,并且将遵循相同的配置設定/取消配置設定過程。
spam9
C++ overloaded functions, methods, and constructors are mostly supported by SWIG . For example, if you have two functions like this:
SWIG 支援巨大部分 C++ 重載函數、方法和構造函數。例如,如果你有兩個這樣的函數:
void foo(int);
void foo(char *c);
You can use them in Python in a straightforward manner:
你可以在 Python 以直接的方式使用它們:
>>> foo(3) # foo(int)
>>> foo("Hello") # foo(char *c)
Similarly, if you have a class like this,
類似的,如果你有一個這樣的類,
class Foo {
public:
Foo();
Foo(const Foo &);
...
};
you can write Python code like this:
你可以這樣寫 Python 代碼
>>> f = Foo() # Create a Foo
>>> g = Foo(f) # Copy f
Overloading support is not quite as flexible as in C++. Sometimes there are methods that SWIG can't disambiguate. For example:
重載支援不像 C++ 那樣靈活。有時有些方法 SWIG 無法消除歧義。例如:
void spam(int);
void spam(short);
void foo(Bar *b);
void foo(Bar &b);
If declarations such as these appear, you will get a warning message like this:
如果出現了這些聲明,你活收到一條警告:
example.i:12: Warning 509: Overloaded method spam(short) effectively ignored,
example.i:11: Warning 509: as it is shadowed by spam(int).
To fix this, you either need to ignore or rename one of the methods. For example:
為了修正,你可以省略或重命名其中一個方法。例如:
%rename(spam_short) spam(short);
...
void spam(int);
void spam(short); // Accessed as spam_short
%ignore spam(short);
...
void spam(int);
void spam(short); // Ignored
SWIG resolves overloaded functions and methods using a disambiguation scheme that ranks and sorts declarations according to a set of type-precedence rules. The order in which declarations appear in the input does not matter except in situations where ambiguity arises--in this case, the first declaration takes precedence.
Please refer to the "SWIG and C++" chapter for more information about overloading.
SWIG 使用消除歧義的方案來解析重載的函數和方法,該方案根據一組類型優先規則對聲明進行排序和排序。聲明出現在輸入中的順序無關緊要,除非出現歧義的情況——在這種情況下,第一個聲明優先。
請參閱 SWIG 和 C++ 一章以擷取有關重載的更多資訊。
Certain C++ overloaded operators can be handled automatically by SWIG . For example, consider a class like this:
SWIG 可以自動處理某些 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);
Complex operator+=(const Complex &c) const;
Complex operator+(const Complex &c) const;
Complex operator-(const Complex &c) const;
Complex operator*(const Complex &c) const;
Complex operator-() const;
double re() const { return rpart; }
double im() const { return ipart; }
};
When wrapped, it works like you expect:
包裝後将像你預期的一樣運作:
>>> c = Complex(3, 4)
>>> d = Complex(7, 8)
>>> e = c + d
>>> e.re()
10.0
>>> e.im()
12.0
>>> c += d
>>> c.re()
10.0
>>> c.im()
12.0
One restriction with operator overloading support is that SWIG is not able to fully handle operators that aren't defined as part of the class. For example, if you had code like this
支援運算符重載有一個限制,那就是 SWIG 無法完全處理未定義成類的一部分的運算符。例如,如果你有這樣的代碼
class Complex {
...
friend Complex operator+(double, const Complex &c);
...
};
then SWIG ignores it and issues a warning. You can still wrap the operator, but you may have to encapsulate it in a special function. For example:
然後 SWIG 會忽略它并發出警告。你仍然可以包裝運算符,但是可能必須将其封裝在特殊函數中。例如:
%rename(Complex_add_dc) operator+(double, const Complex &);
There are ways to make this operator appear as part of the class using the
%extend
directive. Keep reading.
Also, be aware that certain operators don't map cleanly to Python. For instance, overloaded assignment operators don't map to Python semantics and will be ignored.
有多種方法可以使用
%extend
指令使該運算符出現在類中。繼續閱讀。
另外,請注意,某些運算符不能完全映射到 Python。例如,重載的指派運算符不會映射到 Python 語義,是以将被忽略。
SWIG is aware of C++ namespaces, but namespace names do not appear in the module nor do namespaces result in a module that is broken up into submodules or packages. For example, if you have a file like this,
SWIG 知道 C++ 命名空間,但是命名空間名稱不會出現在子產品中,命名空間也不會導緻子產品分解為子子產品或程式包。例如,如果你有一個像這樣的檔案,
%module example
namespace foo {
int fact(int n);
struct Vector {
double x, y, z;
};
};
it works in Python as follows:
在 Python 中它将如下運作:
>>> import example
>>> example.fact(3)
6
>>> v = example.Vector()
>>> v.x = 3.4
>>> print v.y
0.0
>>>
If your program has more than one namespace, name conflicts (if any) can be resolved using
%rename
For example:
如果你的程式有多個命名空間,則可以使用 %rename
解決名稱沖突(如果有的話),例如:
%rename(Bar_spam) Bar::spam;
namespace Foo {
int spam();
}
namespace Bar {
int spam();
}
If you have more than one namespace and your want to keep their symbols separate, consider wrapping them as separate SWIG modules. For example, make the module name the same as the namespace and create extension modules for each namespace separately. If your program utilizes thousands of small deeply nested namespaces each with identical symbol names, well, then you get what you deserve.
如果你有多個命名空間,并且想要将它們的符号分開,請考慮将它們包裝為單獨的 SWIG 子產品。例如,使子產品名稱與命名空間相同,并分别為每個命名空間建立擴充子產品。如果你的程式利用了數千個深層嵌套的小型命名空間,每個命名空間都具有相同的符号名稱,那麼你是自作自受。
C++ templates don't present a huge problem for SWIG . However, in order to create wrappers, you have to tell SWIG to create wrappers for a particular template instantiation. To do this, you use the
%template
對于 SWIG,C++ 模闆不會帶來很大的問題。但是,為了建立包裝器,必須告訴 SWIG 為特定的模闆執行個體建立包裝器。為此,你可以使用 %template
%module example
%{
#include "pair.h"
%}
template<class T1, class T2>
struct pair {
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair();
pair(const T1&, const T2&);
~pair();
};
%template(pairii) pair<int, int>;
In Python:
在 Python 中:
>>> import example
>>> p = example.pairii(3, 4)
>>> p.first
3
>>> p.second
4
Obviously, there is more to template wrapping than shown in this example. More details can be found in the SWIG and C++ chapter. Some more complicated examples will appear later.
顯然,模闆包裝的實際内容比本示例中所示的更多。可以在 SWIG 和 C++ 一章中找到更多詳細資訊。一些更複雜的示例将在以後出現。
36.3.14.1 shared_ptr
智能指針
shared_ptr
The C++11 standard provides
std::shared_ptr
which was derived from the Boost implementation,
boost::shared_ptr
. Both of these are available for Python in the SWIG library and usage is outlined in the shared_ptr smart pointer library section.
C++ 11 标準提供了,它是從 Boost 實作的
std::shared_ptr
派生的。兩者在 SWIG 庫中均可用于 Python,
boost::shared_ptr
智能指針庫部分中概述了它們的用法。
shared_ptr
In certain C++ programs, it is common to use classes that have been wrapped by so-called "smart pointers." Generally, this involves the use of a template class that implements
operator->()
在某些 C++ 程式中,通常使用被所謂的“智能指針”包裝的類。通常,這涉及使用實作 operator->()
的模闆類,如下所示:
template<class T> class SmartPtr {
...
T *operator->();
...
}
Then, if you have a class like this,
你有這樣一個類,
class Foo {
public:
int x;
int bar();
};
A smart pointer would be used in C++ as follows:
C++ 中的智能指針如下這樣使用:
SmartPtr<Foo> p = CreateFoo(); // Created somehow (not shown)
...
p->x = 3; // Foo::x
int y = p->bar(); // Foo::bar
To wrap this in Python, simply tell SWIG about the
SmartPtr
class and the low-level
Foo
object. Make sure you instantiate
SmartPtr
using
%template
if necessary. For example:
要将其包裝在 Python 中,隻需将其類和底層
SmartPtr
對象告訴 SWIG 。確定必要時使用
Foo
執行個體化
%template
。例如:
SmartPtr
%module example
...
%template(SmartPtrFoo) SmartPtr<Foo>;
...
Now, in Python, everything should just "work":
現在,Python 中一切将如下運作:
>>> p = example.CreateFoo() # Create a smart-pointer somehow
>>> p.x = 3 # Foo::x
>>> p.bar() # Foo::bar
If you ever need to access the underlying pointer returned by
operator->()
itself, simply use the
__deref__()
method. For example:
如果你需要自行通路傳回的底層指針,隻需使用
operator->()
方法。例如:
__deref__()
>>> f = p.__deref__() # Returns underlying Foo *
The C++ reference counted objects section contains Python examples of memory management using referencing counting.
C++ 引用計數對象包含使用引用計數進行記憶體管理的 Python 示例。
In the previous section, a high-level view of Python wrapping was presented. A key component of this wrapping is that structures and classes are wrapped by Python proxy classes. This provides a very natural Python interface and allows SWIG to support a number of advanced features such as operator overloading. However, a number of low-level details were omitted. This section provides a brief overview of how the proxy classes work.
New in SWIG version 2.0.4: The use of Python proxy classes has performance implications that may be unacceptable for a high-performance library. The new
-builtin
option instructs SWIG to forego the use of proxy classes, and instead create wrapped types as new built-in Python types. When this option is used, the following section ("Proxy classes") does not apply. Details on the use of the
-builtin
option are in the Built-in Types section.
在上一節中,提供了 Python 包裝的進階視圖。這種包裝的關鍵部分是結構體和類由 Python 代理類包裝。這提供了非常自然的 Python 接口,并允許 SWIG 支援許多進階功能,例如運算符重載。但是,省略了許多底層細節。本節簡要概述了代理類的工作方式。
SWIG 版本 2.0.4 中的新增功能:使用 Python 代理類可能會對性能産生影響,這對于高性能庫而言可能是無法接受的。新的
選項訓示 SWIG 放棄使用代理類,而是将包裝的類型建立為新的内置 Python 類型。使用此選項時,以下部分(“代理類”)不适用。有關使用
-builtin
選項的詳細資訊,請參見内置類型部分。
-builtin
In the "SWIG basics" and " SWIG and C++" chapters, details of low-level structure and class wrapping are described. To summarize those chapters, if you have a class like this
在“SWIG 基礎知識”和 “SWIG 和 C++”一章,介紹了底層結構體和類包裝的詳細資訊。總結這些章節,如果你有這樣的類
class Foo {
public:
int x;
int spam(int);
...
then SWIG transforms it into a set of low-level procedural wrappers. For example:
然後,SWIG 将其轉換為一組低級程式包裝器。例如:
Foo *new_Foo() {
return new Foo();
}
void delete_Foo(Foo *f) {
delete f;
}
int Foo_x_get(Foo *f) {
return f->x;
}
void Foo_x_set(Foo *f, int value) {
f->x = value;
}
int Foo_spam(Foo *f, int arg1) {
return f->spam(arg1);
}
These wrappers can be found in the low-level extension module (e.g.,
_example
Using these wrappers, SWIG generates a high-level Python proxy class (also known as a shadow class) like this (shown for Python 2.2):
這些包裝器可以在低級擴充子產品(例如
_example
)中找到。
使用這些包裝器,SWIG 生成了這樣的進階 Python 代理類(也稱為影子類)(針對 Python 2.2 顯示):
import _example
class Foo(object):
def__init__(self):
self.this = _example.new_Foo()
self.thisown = 1
def__del__(self):
if self.thisown:
_example.delete_Foo(self.this)
def spam(self, arg1):
return _example.Foo_spam(self.this, arg1)
x = property(_example.Foo_x_get, _example.Foo_x_set)
This class merely holds a pointer to the underlying C++ object (
.this
) and dispatches methods and member variable access to that object using the low-level accessor functions. From a user's point of view, it makes the class work normally:
此類僅持有指向底層 C++ 對象( .this
)的指針,并使用低級通路器函數将方法和成員變量通路權配置設定給該對象。從使用者的角度來看,它使類正常工作:
>>> f = example.Foo()
>>> f.x = 3
>>> y = f.spam(5)
The fact that the class has been wrapped by a real Python class offers certain advantages. For instance, you can attach new Python methods to the class and you can even inherit from it (something not supported by Python built-in types until Python 2.2).
該類已經被真正的 Python 類包裝了,這一事實提供了某些優勢。例如,你可以将新的 Python 方法附加到該類,甚至可以從該類繼承(Python 内置類型直到 Python 2.2 才支援該功能)。
-builtin
option provides a significant performance improvement in the wrapped code. To understand the difference between proxy classes and built-in types, let's take a look at what a wrapped object looks like under both circumstances.
When proxy classes are used, each wrapped object in python is an instance of a pure python class. As a reminder, here is what the
__init__
method looks like in a proxy class:
-builtin
選項在包裝的代碼中顯着提高了性能。為了了解代理類和内置類型之間的差別,讓我們看一下兩種情況下包裝對象的外觀。
使用代理類時,Python 中的每個包裝對象都是純 python 類的執行個體。提醒一下,這是
方法在代理類中的樣子:
__init__
class Foo(object):
def__init__(self):
self.this = _example.new_Foo()
self.thisown = 1
When a
Foo
instance is created, the call to
_example.new_Foo()
creates a new C++
Foo
instance; wraps that C++ instance inside an instance of a python built-in type called
SWIG PyObject
; and stores the
SWIG PyObject
instance in the 'this' field of the python Foo object. Did you get all that? So, the python
Foo
object is composed of three parts:
- The python
instance, which contains...Foo
- ... an instance of
, which contains...struct SWIG PyObject
- ... a C++
instanceFoo
-builtin
is used, the pure python layer is stripped off. Each wrapped class is turned into a new python built-in type which inherits from
SWIG PyObject
SWIG PyObject
instances are returned directly from the wrapped methods. For more information about python built-in extensions, please refer to the python documentation: http://docs.python.org/extending/newtypes.html
建立執行個體後,對
Foo
的調用将建立一個新的 C++
_example.new_Foo()
執行個體;将 C++ 執行個體包裝在名為
Foo
的 Python 内置類型的執行個體中;并将
SWIG PyObject
執行個體存儲在 python
SWIG PyObject
對象的
Foo
字段中。你明白了嗎? 是以,Python 的
this
對象由三部分組成:
Foo
當使用
- python
執行個體,其中包含...
Foo
- ...結構體
的執行個體,其中包含...
SWIG PyObject
- ...一個 C++
執行個體
Foo
時,純 python 層被剝離。每個包裝的類都轉換為繼承自
-builtin
的新的 Python 内置類型,并且
SWIG PyObject
執行個體直接從包裝的方法中傳回。有關 Python 内置擴充的更多資訊,請參考 python 文檔:http://docs.python.org/extending/newtypes.html
SWIG PyObject
Use of the
-builtin
option implies a couple of limitations:
- python version support:
- Versions 2.5 and up are fully supported
- Versions 2.3 and 2.4 are mostly supported; there are problems with director classes and/or sub-classing a wrapped type in python.
- Versions older than 2.3 are not supported.
- Some legacy syntax is no longer supported; in particular:
- The functional interface is no longer exposed. For example, you may no longer call
. Instead, you must useWhizzo.new_CrunchyFrog()
Whizzo.CrunchyFrog()
- Static member variables are no longer accessed through the 'cvar' field (e.g.,
). They are instead accessed in the idiomatic way (Dances.cvar.FishSlap
Dances.FishSlap
- The functional interface is no longer exposed. For example, you may no longer call
- Wrapped types may not be raised as python exceptions. Here's why: the python internals expect that all sub-classes of Exception will have this struct layout:
使用選項意味着兩個限制:
-builtin
- python 版本支援:
- 完全支援 2.5 版及更高版本
- 主要支援 2.3 和 2.4版本;Director 類和/或在 Python 中包裝類型的子類存在問題。
- 不支援 2.3 之前的版本。
- 不再支援某些舊式文法;尤其是:
- 函數接口不再暴露。例如,你可能不再調用
。相反,你必須使用
Whizzo.new_CrunchyFrog()
Whizzo.CrunchyFrog()
- 靜态成員變量不再通過
字段通路(例如,
cvar
)。而是以慣用的方式通路它們(
Dances.cvar.FishSlap
Dances.FishSlap
- 包裝類型不能作為 python 異常引發。原因如下:python 内部希望
的所有子類都具有以下結構體布局:
Exception
typedef struct {
PyObject_HEAD
PyObject *dict;
PyObject *args;
PyObject *message;
} PyBaseExceptionObject;
But SWIG-generated wrappers expect that all SWIG-wrapped classes will have this struct layout:
但是,用 SWIG 生成的包裝器希望所有用 SWIG 包裹的類都具有以下結構體布局:
typedef struct {
PyObject_HEAD
void *ptr;
SWIG_type_info *ty;
int own;
PyObject *next;
PyObject *dict;
} SWIG PyObject;
There are workarounds for this. For example, if you wrap this class:
有解決方法。例如,如果包裝這個類:
class MyException {
public:
MyException (const char *msg_);
~MyException ();
const char *what () const;
private:
char *msg;
};
... you can define this python class, which may be raised as an exception:
...你可以定義此 python 類,可以引發異常:
class MyPyException(Exception):
def__init__(self, msg, *args):
Exception.__init__(self, *args)
self.myexc = MyException(msg)
def what(self):
return self.myexc.what()
- Reverse binary operators (e.g.,
) are not supported.__radd__
To illustrate this point, if you have a wrapped class called
MyString
, and you want to use instances of
MyString
interchangeably with native python strings, you can define an
'operator+ (const char*)'
method :
為了說明這一點,如果你有一個名為
- 逆向二進制運算符(例如
)不被支援。
__radd__
的包裝類,并且想将
MyString
的執行個體與本地 python 字元串互換使用,則可以定義一個
MyString
方法:
operator +(const char *)
class MyString {
public:
MyString (const char *init);
MyString operator+ (const char *other) const;
...
};
SWIG will automatically create an operator overload in python that will allow this:
SWIG 将自動地在 Python 中建立一個運算符重載,允許這樣操作:
from MyModule import MyString
mystr = MyString("No one expects")
episode = mystr + " the Spanish Inquisition"
This works because the first operand (
mystr
) defines a way to add a native string to itself. However, the following will not work:
之是以有效,是因為第一個操作數( mystr
)定義了一種向其自身添加本地字元串的方法。但是,以下内容将不起作用:
from MyModule import MyString
mystr = MyString("Parrot")
episode = "Dead " + mystr
The above code fails, because the first operand -- a native python string -- doesn't know how to add an instance of
MyString
to itself.
- If you have multiple SWIG modules that share type information (more info), the
option requires a bit of extra discipline to ensure that base classes are initialized before derived classes. Specifically:-builtin
- There must be an unambiguous dependency graph for the modules.
- Module dependencies must be explicitly stated with
statements in the SWIG interface file.%import
As an example, suppose module
A
has this interface in
A.i
:
上面的代碼失敗了,因為第一個操作數,一個本地 python 字元串,不知道如何向自己添加的執行個體。
MyString
例如,假設子產品
- 如果你有多個共享類型資訊的 SWIG 子產品(更多資訊),則
選項需要一些額外的規則,以確定在派生類之前初始化基類。特别:
-builtin
- 子產品必須有明确的依賴關系圖。
- 必須在 SWIG 接口檔案中使用
聲明明确聲明子產品依賴性。
%import
在
A
中具有此接口:
A.i
%module "A";
class Base {
...
};
If you want to wrap another module containing a class that inherits from
A
, this is how it would look :
如果要包裝另一個包含從 A
繼承的類的子產品,它将是這樣:
%module "B";
%import "A.i"
class Derived : public Base {
...
};
import "A.i"
statement is required, because module
B
depends on module
A
As long as you obey these requirements, your python code may import the modules in any order :
由于子產品依賴于子產品
B
,是以必須使用
A
import A.i
聲明。
隻要你遵守這些要求,你的 Python 代碼就可以按任何順序導入子產品:
import B
import A
assert(issubclass(B.Derived, A.Base))
The entire justification for the
-builtin
option is improved performance. To that end, the best way to squeeze maximum performance out of your wrappers is to use operator overloads. Named method dispatch is slow in python, even when compared to other scripting languages. However, python built-in types have a large number of "slots", analogous to C++ operator overloads, which allow you to short-circuit named method dispatch for certain common operations.
By default, SWIG will translate most C++ arithmetic operator overloads into python slot entries. For example, suppose you have this class:
-builtin
選項的全部理由是為了提高性能。為此,從包裝程式中擷取最大性能的最佳方法是使用運算符重載。即使與其他腳本語言相比,在 Python 中命名方法的分發也很慢。但是,python 内置類型具有大量的“slot”,類似于 C++ 運算符重載,這使你可以為某些常用操作短路命名方法分派。
預設情況下,SWIG 會将大多數 C++ 算術運算符重載轉換為 python slot。例如,假設你有此類:
class Twit {
public:
Twit operator+ (const Twit& twit) const;
// Forward to operator+
Twit add (const Twit& twit) const
{ return *this + twit; }
};
SWIG will automatically register
operator+
as a python slot operator for addition. You may write python code like this:
SWIG 将自動将 operator+
注冊為 python slot 運算符以進行添加。你可以這樣編寫 Python 代碼:
from MyModule import Twit
nigel = Twit()
emily = Twit()
percival = nigel + emily
percival = nigel.add(emily)
The last two lines of the python code are equivalent, but the line that uses the '+' operator is much faster.
In-place operators (e.g.,
operator+=
) and comparison operators (
operator==, operator<
, etc.) are also converted to python slot operators. For a complete list of C++ operators that are automatically converted to python slot operators, refer to the file
python/pyopers. SWIG
in the SWIG library.
Read about all of the available python slots here: http://docs.python.org/c-api/typeobj.html
There are two ways to define a python slot function: dispatch to a statically defined function; or dispatch to a method defined on the operand.
To dispatch to a statically defined function, use
%feature("python:<slot>")
, where
<slot>
is the name of a field in a
PyTypeObject, PyNumberMethods, PyMappingMethods, PySequenceMethods
PyBufferProcs
. You may override (almost) all of these slots.
Let's consider an example setting the
tp_hash
slot for the
MyClass
type. This is akin to providing a
__hash__
method (for non-builtin types) to make a type hashable. The hashable type can then for example be added to a Python
dict
Python 代碼的最後兩行是等效的,但是使用
+
運算符的行要快得多。
本地運算符(例如
)和比較運算符(
operator +=
operator ==
等)也将轉換為 python slot 運算符。有關自動轉換為 python slot 運算符的 C++ 運算符的完整清單,請參見 SWIG 庫中的檔案
operator <
python/pyopers.SWIG
在此處閱讀有關所有可用 Python slot 的資訊:http://docs.python.org/c-api/typeobj.html
定義 python slot 函數有兩種方法:配置設定給靜态定義的函數;或排程到在操作數上定義的方法。
要分派到靜态定義的函數,請使用
,其中
%feature("python:<slot>")
是
<slot>
PyTypeObject
PyNumberMethods
PyMappingMethods
PySequenceMethods
PyBufferProcs
中的字段名稱。你可以覆寫(幾乎)所有這些 slot。
讓我們考慮一個為
類型設定
MyClass
slot 的示例。這類似于提供
tp_hash
方法(用于非内置類型)以使類型可哈希化。然後可以将可哈希類型添加到 Python 的
__hash__
中。
dict
%feature("python:tp_hash") MyClass "myHashFunc";
class MyClass {
public:
long field1;
long field2;
...
};
%{
#if PY_VERSION_HEX >= 0x03020000
static Py_hash_t myHashFunc(PyObject *pyobj)
#else
static long myHashFunc(PyObject *pyobj)
#endif
{
MyClass *cobj;
// Convert pyobj to cobj
return (cobj->field1 * (cobj->field2 << 7));
}
%}
If you examine the generated code, the supplied hash function will now be the function callback in the tp_hash slot for the builtin type for
MyClass
如果檢查生成的代碼,現在提供的哈希函數将成為slot 中
tp_hash
内置類型的函數回調:
MyClass
static PyHeapTypeObject SWIG PyBuiltin__MyClass_type = {
...
(hashfunc) myHashFunc, /* tp_hash */
...
NOTE: It is the responsibility of the programmer (that's you!) to ensure that a statically defined slot function has the correct signature, the
hashfunc
typedef in this case.
If, instead, you want to dispatch to an instance method, you can use %feature("python:slot"). For example:
注意:程式員(就是你!)的責任是確定靜态定義的 slot 函數具有正确的簽名,在這種情況下為相反,如果要分派到執行個體方法,則可以使用
hashfunc
%feature(python:slot)
%feature("python:slot", "tp_hash", functype="hashfunc") MyClass::myHashFunc;
#if PY_VERSION_HEX < 0x03020000
#define Py_hash_t long
#endif
class MyClass {
public:
Py_hash_t myHashFunc() const;
...
};
NOTE: Some python slots use a method signature which does not match the signature of SWIG-wrapped methods. For those slots, SWIG will automatically generate a "closure" function to re-marshal the arguments before dispatching to the wrapped method. Setting the "functype" attribute of the feature enables SWIG to generate the chosen closure function.
There is further information on
%feature("python:slot")
in the file
python/pyopers. SWIG
注意:某些 python slot 使用的方法簽名與 SWIG 封裝的方法的簽名不比對。對于這些 slot,SWIG 将自動生成一個“閉包”函數,以在将其分派到包裝的方法之前重新編組參數。設定功能的
functype
屬性可使 SWIG 生成所選的關閉函數。
SWIG 庫中的檔案
中有關于
python/pyopers.SWIG
的更多資訊。
%feature(python:slot)
NOTE: Although this section refers to proxy objects, everything here also applies when the
-builtin
option is used.
Associated with proxy object, is an ownership flag
.thisown
The value of this flag determines who is responsible for deleting the underlying C++ object. If set to 1, the Python interpreter will destroy the C++ object when the proxy class is garbage collected. If set to 0 (or if the attribute is missing), then the destruction of the proxy class has no effect on the C++ object.
When an object is created by a constructor or returned by value, Python automatically takes ownership of the result. For example:
注意:盡管本節涉及代理對象,但是當使用
-builtin
選項時,此處的所有内容也适用。
所有權标志
.thisown
與代理對象相關聯,該标志的值确定誰負責删除基礎 C++ 對象。如果設定為 1,則在垃圾回收代理類時,Python 解釋器将銷毀 C++ 對象。如果設定為 0(或缺少屬性),則代理類的銷毀對 C++ 對象無效。
當對象由構造函數建立或由值傳回時,Python 自動擷取結果的所有權。例如:
class Foo {
public:
Foo();
Foo bar();
};
>>> f = Foo()
>>> f.thisown
1
>>> g = f.bar()
>>> g.thisown
1
On the other hand, when pointers are returned to Python, there is often no way to know where they came from. Therefore, the ownership is set to zero. For example:
另一方面,當指針傳回到 Python 時,通常沒有辦法知道它們來自何處。是以,所有權設定為零。例如:
class Foo {
public:
...
Foo *spam();
...
};
>>> f = Foo()
>>> s = f.spam()
>>> print s.thisown
0
>>>
This behavior is especially important for classes that act as containers. For example, if a method returns a pointer to an object that is contained inside another object, you definitely don't want Python to assume ownership and destroy it!
A good way to indicate that ownership should be set for a returned pointer is to use the %newobject directive.
Related to containers, ownership issues can arise whenever an object is assigned to a member or global variable. For example, consider this interface:
對于充當容器的類,此行為尤其重要。例如,如果方法傳回指向另一個對象内包含的對象的指針,則你絕對不希望 Python 承擔所有權并銷毀它!
訓示應為傳回的指針設定所有權的一種好方法是使用
%newobject
指令。
與容器相關的是,隻要将對象配置設定給成員或全局變量,就會出現所有權問題。例如,考慮以下接口:
%module example
struct Foo {
int value;
Foo *next;
};
Foo *head = 0;
When wrapped in Python, careful observation will reveal that ownership changes whenever an object is assigned to a global variable. For example:
當用 Python 封裝時,仔細觀察會發現,隻要将對象配置設定給全局變量,所有權就會發生變化。例如:
>>> f = example.Foo()
>>> f.thisown
1
>>> example.cvar.head = f
>>> f.thisown
0
>>>
In this case, C is now holding a reference to the object---you probably don't want Python to destroy it. Similarly, this occurs for members. For example:
在這種情況下,C 現在持有對該對象的引用,你可能不希望 Python 銷毀它。同樣,這對于成員也是如此。例如:
>>> f = example.Foo()
>>> g = example.Foo()
>>> f.thisown
1
>>> g.thisown
1
>>> f.next = g
>>> g.thisown
0
>>>
For the most part, memory management issues remain hidden. However, there are occasionally situations where you might have to manually change the ownership of an object. For instance, consider code like this:
在大多數情況下,記憶體管理問題仍然隐藏。但是,在某些情況下,你可能必須手動更改對象的所有權。例如,考慮如下代碼:
class Node {
Object *value;
public:
void set_value(Object *v) { value = v; }
...
};
Now, consider the following Python code:
現在考慮下面的 Python 代碼:
>>> v = Object() # Create an object
>>> n = Node() # Create a node
>>> n.set_value(v) # Set value
>>> v.thisown
1
>>> del v
In this case, the object
n
is holding a reference to
v
internally. However, SWIG has no way to know that this has occurred. Therefore, Python still thinks that it has ownership of the object. Should the proxy object be destroyed, then the C++ destructor will be invoked and
n
will be holding a stale-pointer. If you're lucky, you will only get a segmentation fault.
To work around this, it is always possible to flip the ownership flag. For example,
在這種情況下,對象在内部持有對
n
的引用。但是,SWIG 無法知道發生了這種情況。是以,Python 仍然認為它擁有對象的所有權。如果代理對象被銷毀,則将調用 C++ 析構函數,并且
v
n
将持有舊指針。如果幸運的話,你隻會遇到分段錯誤。
要解決此問題,始終可以翻轉所有權标志。例如,
>>> v.thisown = 0
It is also possible to deal with situations like this using typemaps--an advanced topic discussed later.
還可以使用類型映射處理這種情況,這是稍後讨論的進階主題。
SWIG makes every attempt to preserve backwards compatibility with older versions of Python to the extent that it is possible. However, in Python-2.2, an entirely new type of class system was introduced. This new-style class system offers many enhancements including static member functions, properties (managed attributes), and class methods. Details about all of these changes can be found on www.python.org and is not repeated here.
To address differences between Python versions, SWIG currently emits dual-mode proxy class wrappers. In Python-2.2 and newer releases, these wrappers encapsulate C++ objects in new-style classes that take advantage of new features (static methods and properties). However, if these very same wrappers are imported into an older version of Python, old-style classes are used instead.
This dual-nature of the wrapper code means that you can create extension modules with SWIG and those modules will work with all versions of Python ranging from Python-2.0 to the very latest release. Moreover, the wrappers take advantage of Python-2.2 features when available.
For the most part, the interface presented to users is the same regardless of what version of Python is used. The only incompatibility lies in the handling of static member functions. In Python-2.2, they can be accessed via the class itself. In Python-2.1 and earlier, they have to be accessed as a global function or through an instance (see the earlier section).
SWIG 盡一切可能保持與舊版本 Python 的向後相容性。但是,在 Python-2.2 中,引入了一種全新的類系統。這種新型的類系統提供了許多增強功能,包括靜态成員函數、屬性(托管屬性)和類方法。有關所有這些更改的詳細資訊可以在 www.python.org 上找到,在此不再贅述。
為了解決 Python 版本之間的差異,SWIG 目前發出雙模式代理類包裝器。在 Python-2.2 和更高版本中,這些包裝器将 C++ 對象封裝在利用新功能(靜态方法和屬性)的新型類中。但是,如果将這些完全相同的包裝器導入到舊版本的 Python 中,則會使用舊式類。
包裝代碼的這種雙重特性意味着你可以使用 SWIG 建立擴充子產品,并且這些子產品将與從 Python-2.0 到最新版本的所有 Python 版本一起使用。而且,包裝器在可用時會利用 Python-2.2 功能。
在大多數情況下,無論使用什麼版本的 Python,呈現給使用者的接口都是相同的。唯一的不相容性在于靜态成員函數的處理。在 Python-2.2 中,可以通過類本身通路它們。在 Python-2.1 和更早版本中,必須将它們作為全局函數或通過執行個體進行通路(請參見前面的部分)。
Proxy classes provide a more natural, object-oriented way to access extension classes. As described above, each proxy instance has an associated C++ instance, and method calls to the proxy are passed to the C++ instance transparently via C wrapper functions.
This arrangement is asymmetric in the sense that no corresponding mechanism exists to pass method calls down the inheritance chain from C++ to Python. In particular, if a C++ class has been extended in Python (by extending the proxy class), these extensions will not be visible from C++ code. Virtual method calls from C++ are thus not able access the lowest implementation in the inheritance chain.
Changes have been made to SWIG 1.3.18 to address this problem and make the relationship between C++ classes and proxy classes more symmetric. To achieve this goal, new classes called directors are introduced at the bottom of the C++ inheritance chain. The job of the directors is to route method calls correctly, either to C++ implementations higher in the inheritance chain or to Python implementations lower in the inheritance chain. The upshot is that C++ classes can be extended in Python and from C++ these extensions look exactly like native C++ classes. Neither C++ code nor Python code needs to know where a particular method is implemented: the combination of proxy classes, director classes, and C wrapper functions takes care of all the cross-language method routing transparently.
代理類提供了一種更自然、更面向對象的方法來通路擴充類。如上所述,每個代理執行個體都有一個關聯的 C++ 執行個體,并且對代理的方法調用通過 C 包裝函數透明地傳遞給 C++ 執行個體。
這種安排是不對稱的,因為不存在從 C++ 到 Python 的繼承鍊上傳遞方法調用的相應機制。特别是,如果已經在 Python 中擴充了 C++ 類(通過擴充代理類),則這些擴充将從 C++ 代碼中不可見。是以,來自 C++ 的虛方法調用無法通路繼承鍊中最低的實作。
已對 SWIG 1.3.18 進行了更改,以解決此問題并使 C++ 類和代理類之間的關系更加對稱。為了實作此目标,在 C++ 繼承鍊的底部引入了稱為 Director 的新類。Director 的工作是将方法調用正确地路由到繼承鍊中較高的 C++ 實作或繼承鍊中較低的 Python 實作。結果是可以在 Python 中擴充 C++ 類,而從 C++ 中擴充這些擴充看起來與本地 C++ 類完全一樣。C++ 代碼和 Python 代碼都不需要知道在哪裡實作特定方法:代理類、Director 類和 C 包裝函數的組合可透明地處理所有跨語言方法路由。
The director feature is disabled by default. To use directors you must make two changes to the interface file. First, add the "directors" option to the %module directive, like this:
Director 功能預設情況下處于禁用狀态。要使用 Director,必須對接口檔案進行兩項更改。首先,将選項添加到
directors
指令,如下所示:
%module
%module(directors="1") modulename
Without this option no director code will be generated. Second, you must use the %feature("director") directive to tell SWIG which classes and methods should get directors. The %feature directive can be applied globally, to specific classes, and to specific methods, like this:
如果沒有此選項,将不會生成 Director 代碼。其次,你必須使用指令來告訴 SWIG 哪些類和方法應該獲得 Director 。
%feature("director")
指令可以全局應用于特定的類和特定的方法,如下所示:
%feature
// generate directors for all classes that have virtual methods
%feature("director");
// generate directors for all virtual methods in class Foo
%feature("director") Foo;
You can use the %feature("nodirector") directive to turn off directors for specific classes or methods. So for example,
你可以使用 %feature("nodirector")
指令關閉特定類或方法的控制器。例如
%feature("director") Foo;
%feature("nodirector") Foo::bar;
will generate directors for all virtual methods of class Foo except bar().
Directors can also be generated implicitly through inheritance. In the following, class Bar will get a director class that handles the methods one() and two() (but not three()):
将為類的所有虛方法(除
Foo
bar()
之外)生成 Director 。
Director 也可以通過繼承隐式生成。在下面的代碼中,
類将獲得一個處理方法
Bar
one()
(但不是
two()
)的 Director 類:
three()
%feature("director") Foo;
class Foo {
public:
Foo(int foo);
virtual ~Foo();
virtual void one();
virtual void two();
};
class Bar: public Foo {
public:
virtual void three();
};
then at the python side you can define
在 python 端你可以定義
import mymodule
class MyFoo(mymodule.Foo):
def __init__(self, foo):
mymodule.Foo.__init__(self, foo)
# super().__init__(foo) # Alternative construction for Python3
def one(self):
print "one from python"
For each class that has directors enabled, SWIG generates a new class that derives from both the class in question and a special
SWIG::Director
class. These new classes, referred to as director classes, can be loosely thought of as the C++ equivalent of the Python proxy classes. The director classes store a pointer to their underlying Python object and handle various issues related to object ownership. Indeed, this is quite similar to the "this" and "thisown" members of the Python proxy classes.
For simplicity let's ignore the
SWIG::Director
class and refer to the original C++ class as the director's base class. By default, a director class extends all virtual methods in the inheritance chain of its base class (see the preceding section for how to modify this behavior). Thus all virtual method calls, whether they originate in C++ or in Python via proxy classes, eventually end up in at the implementation in the director class. The job of the director methods is to route these method calls to the appropriate place in the inheritance chain. By "appropriate place" we mean the method that would have been called if the C++ base class and its extensions in Python were seamlessly integrated. That seamless integration is exactly what the director classes provide, transparently skipping over all the messy extension API glue that binds the two languages together.
In reality, the "appropriate place" is one of only two possibilities: C++ or Python. Once this decision is made, the rest is fairly easy. If the correct implementation is in C++, then the lowest implementation of the method in the C++ inheritance chain is called explicitly. If the correct implementation is in Python, the Python API is used to call the method of the underlying Python object (after which the usual virtual method resolution in Python automatically finds the right implementation).
Now how does the director decide which language should handle the method call? The basic rule is to handle the method in Python, unless there's a good reason not to. The reason for this is simple: Python has the most "extended" implementation of the method. This assertion is guaranteed, since at a minimum the Python proxy class implements the method. If the method in question has been extended by a class derived from the proxy class, that extended implementation will execute exactly as it should. If not, the proxy class will route the method call into a C wrapper function, expecting that the method will be resolved in C++. The wrapper will call the virtual method of the C++ instance, and since the director extends this the call will end up right back in the director method. Now comes the "good reason not to" part. If the director method were to blindly call the Python method again, it would get stuck in an infinite loop. We avoid this situation by adding special code to the C wrapper function that tells the director method to not do this. The C wrapper function compares the pointer to the Python object that called the wrapper function to the pointer stored by the director. If these are the same, then the C wrapper function tells the director to resolve the method by calling up the C++ inheritance chain, preventing an infinite loop.
One more point needs to be made about the relationship between director classes and proxy classes. When a proxy class instance is created in Python, SWIG creates an instance of the original C++ class and assigns it to
.this
. This is exactly what happens without directors and is true even if directors are enabled for the particular class in question. When a class derived from a proxy class is created, however, SWIG then creates an instance of the corresponding C++ director class. The reason for this difference is that user-defined subclasses may override or extend methods of the original class, so the director class is needed to route calls to these methods correctly. For unmodified proxy classes, all methods are ultimately implemented in C++ so there is no need for the extra overhead involved with routing the calls through Python.
對于每個啟用了 Director 的類,SWIG 都會生成一個新類,該類從相關類和特殊的類中派生。可以将這些新類(稱為 Director 類)粗略地視為 Python 代理類的 C++ 等效類。Director 類存儲指向其基礎 Python 對象的指針,并處理與對象所有權有關的各種問題。實際上,這與 Python 代理類的
SWIG::Director
this
thisown
成員非常相似。
為了簡單起見,讓我們忽略
SWIG::Director
類,并将原始 C++ 類稱為 Director 的基類。預設情況下,director 類在其基類的繼承鍊中擴充了所有虛方法(有關如何修改此行為的資訊,請參見上一節)。是以,所有虛方法調用,無論它們是源自 C++ 還是源自 Python,都是通過代理類進行的,最終都以 Director 類中的實作結束。Director 方法的工作是将這些方法調用路由到繼承鍊中的适當位置。“适當的位置”是指如果 C++ 基類及其在 Python 中的擴充被無縫內建的話,該方法将被調用。這種無縫內建正是 Director 類所提供的,透明地跳過了将兩種語言綁定在一起的所有混亂擴充 API 膠水。
實際上,“适當的位置”是僅有的兩種可能性之一: C++ 或 Python。一旦做出決定,剩下的就很容易了。如果正确的實作在 C++ 中,則顯式調用 C++ 繼承鍊中該方法的最低實作。如果正确的實作是在 Python 中執行的,則使用 Python API 調用基礎 Python 對象的方法(此後,Python 中通常的虛方法解析會自動找到正确的實作)。
現在,Director 如何決定應使用哪種語言處理方法調用?基本規則是在 Python 中處理該方法,除非有充分的理由不這樣做。原因很簡單:Python 具有該方法最“擴充”的實作。該斷言得到保證,因為至少 Python 代理類實作了該方法。如果所讨論的方法已由派生自代理類的類擴充,則該擴充實作将完全按照應有的方式執行。如果不是,則代理類會将方法調用路由到 C 包裝函數中,期望該方法将在 C++ 中解析。包裝器将調用 C++ 執行個體的虛方法,并且由于 Director 對其進行了擴充,是以該調用将最終傳回到 Director 方法中。現在出現了“拒絕的充分理由”部分。如果 director 方法再次盲目調用 Python 方法,它将陷入無限循環。我們通過在 C 包裝函數中添加特殊代碼來避免這種情況,該函數告訴 Director 方法不要執行此操作。C 包裝函數會将調用包裝函數的 Python 對象的指針與控制器存儲的指針進行比較。如果這些相同,則C 包裝函數将通知主管通過調用 C++ 繼承鍊來解決方法,進而防止無限循環。
關于 Director 類和代理類之間的關系,還需要指出一點。在 Python 中建立代理類執行個體時,SWIG 将建立原始 C++ 類的執行個體并将其配置設定給
。這就是沒有 Director 的情況,即使針對特定類别啟用了 Director,也是如此。但是,當從代理類派生一個類時,SWIG 随後将建立相應的 C++ Director 類的執行個體。造成這種差異的原因是,使用者定義的子類可能會覆寫或擴充原始類的方法,是以需要 Director 類将調用正确路由到這些方法。對于未修改的代理類,所有方法最終都用 C++ 實作,是以不需要通過 Python 路由調用而涉及額外的開銷。
.this
Memory management issues are slightly more complicated with directors than for proxy classes alone. Python instances hold a pointer to the associated C++ director object, and the director in turn holds a pointer back to the Python object. By default, proxy classes own their C++ director object and take care of deleting it when they are garbage collected.
This relationship can be reversed by calling the special
__disown__()
method of the proxy class. After calling this method, the
.thisown
flag is set to zero, and the director class increments the reference count of the Python object. When the director class is deleted it decrements the reference count. Assuming no outstanding references to the Python object remain, the Python object will be destroyed at the same time. This is a good thing, since directors and proxies refer to each other and so must be created and destroyed together. Destroying one without destroying the other will likely cause your program to segfault.
To help ensure that no references to the Python object remain after calling
__disown__()
, this method returns a weak reference to the Python object. Weak references are only available in Python versions 2.1 and higher, so for older versions you must explicitly delete all references. Here is an example:
Director 的記憶體管理問題比僅代理類要複雜得多。Python 執行個體包含指向關聯的 C++ Director 對象的指針,而 Director 又持有指向 Python 對象的指針。預設情況下,代理類擁有其 C++ Director 對象,并在垃圾回收時将其删除。
可以通過調用代理類的特殊
方法來逆轉這種關系。調用此方法後,
__disown__()
.thisown
标志設定為零,并且 Director 類增加 Python 對象的引用計數。删除 Director 類後,它會減少引用計數。假設沒有剩餘的對 Python 對象的引用,則 Python 對象将同時被銷毀。這是一件好事,因為 Director 和代理人互相參照,是以必須一起建立和銷毀。銷毀一個而不破壞另一個可能會導緻你的程式出現段錯誤。
為了幫助確定在調用
之後沒有對 Python 對象的引用,此方法傳回對 Python 對象的弱引用。弱引用僅在 Python 2.1 及更高版本中可用,是以對于較舊的版本,你必須明确删除所有引用。這是一個例子:
__disown__()
class Foo {
public:
...
};
class FooContainer {
public:
void addFoo(Foo *);
...
};
>>> c = FooContainer()
>>> a = Foo().__disown__()
>>> c.addFoo(a)
>>> b = Foo()
>>> b = b.__disown__()
>>> c.addFoo(b)
>>> c.addFoo(Foo().__disown__())
In this example, we are assuming that FooContainer will take care of deleting all the Foo pointers it contains at some point. Note that no hard references to the Foo objects remain in Python.
在此示例中,我們假設将在某個時候删除其包含的所有
FooContainer
指針。請注意,Python 中沒有對
Foo
對象的硬引用。
Foo
With directors routing method calls to Python, and proxies routing them to C++, the handling of exceptions is an important concern. By default, the directors ignore exceptions that occur during method calls that are resolved in Python. To handle such exceptions correctly, it is necessary to temporarily translate them into C++ exceptions. This can be done with the
%feature("director:except")
directive. The following code should suffice in most cases:
Director 将方法調用路由到 Python,并将代理路由到 C++,異常處理是一個重要的問題。預設情況下,控制器将忽略在 Python 中解析的方法調用期間發生的異常。為了正确處理此類異常,有必要将它們臨時轉換為 C++ 異常。這可以通過 %feature("director:except")
指令完成。在大多數情況下,以下代碼就足夠了:
%feature("director:except") {
if ($error != NULL) {
throw SWIG::DirectorMethodException();
}
}
This code will check the Python error state after each method call from a director into Python, and throw a C++ exception if an error occurred. This exception can be caught in C++ to implement an error handler. Currently no information about the Python error is stored in the SWIG::DirectorMethodException object, but this will likely change in the future.
It may be the case that a method call originates in Python, travels up to C++ through a proxy class, and then back into Python via a director method. If an exception occurs in Python at this point, it would be nice for that exception to find its way back to the original caller. This can be done by combining a normal %exception directive with the
director:except
handler shown above. Here is an example of a suitable exception handler:
每次從 Director 調用 Python 的方法後,此代碼将檢查 Python 錯誤狀态,如果發生錯誤,則抛出 C++ 異常。可以在 C++ 中捕獲此異常以實作錯誤處理程式。目前在
SWIG::DirectorMethodException
對象中沒有存儲有關 Python 錯誤的資訊,但是将來可能會改變。
方法調用可能起源于 Python,通過代理類升至 C++,然後通過 Director 方法傳回 Python。如果此時在 Python 中發生異常,那麼該異常可以找到傳回原始調用方的方式會很不錯。這可以通過将普通的
指令與上面顯示的
%exception
處理程式結合使用來完成。這是合适的異常處理程式的示例:
director:except
%exception {
try { $action }
catch ( SWIG::DirectorException &e) { SWIG_fail; }
}
The class SWIG::DirectorException used in this example is actually a base class of SWIG::DirectorMethodException, so it will trap this exception. Because the Python error state is still set when SWIG::DirectorMethodException is thrown, Python will register the exception as soon as the C wrapper function returns.
本示例中使用的類實際上是
SWIG::DirectorException
的基類,是以它将捕獲此異常。由于抛出
SWIG::DirectorMethodException
時仍設定 Python 錯誤狀态,是以 C 包裝函數傳回後,Python将立即注冊異常。
SWIG::DirectorMethodException
Enabling directors for a class will generate a new director method for every virtual method in the class' inheritance chain. This alone can generate a lot of code bloat for large hierarchies. Method arguments that require complex conversions to and from target language types can result in large director methods. For this reason it is recommended that you selectively enable directors only for specific classes that are likely to be extended in Python and used in C++.
Compared to classes that do not use directors, the call routing in the director methods does add some overhead. In particular, at least one dynamic cast and one extra function call occurs per method call from Python. Relative to the speed of Python execution this is probably completely negligible. For worst case routing, a method call that ultimately resolves in C++ may take one extra detour through Python in order to ensure that the method does not have an extended Python implementation. This could result in a noticeable overhead in some cases.
Although directors make it natural to mix native C++ objects with Python objects (as director objects) via a common base class pointer, one should be aware of the obvious fact that method calls to Python objects will be much slower than calls to C++ objects. This situation can be optimized by selectively enabling director methods (using the %feature directive) for only those methods that are likely to be extended in Python.
為類啟用控制器将為該類的繼承鍊中的每個虛方法生成一個新的控制器方法。僅此一項就可以為大型層次結構産生大量代碼膨脹。需要與目智語言類型進行複雜轉換的方法參數可能會導緻使用大型 Director 方法。是以,建議僅針對可能在 Python 中擴充并在 C++ 中使用的特定類,選擇性地啟用 Director。
與不使用 Director 的類相比, Director 方法中的調用路由确實會增加一些開銷。特别是,對于 Python 中的每個方法調用,至少要進行一次動态轉換和一個額外的函數調用。相對于 Python 執行速度,這可能是完全可以忽略的。對于最壞情況的路由,最終用 C++ 解析的方法調用可能會通過 Python 進行額外的繞行,以確定該方法沒有擴充的 Python 實作。在某些情況下,這可能會導緻明顯的開銷。
盡管 Director 自然而然地通過一個通用的基類指針将本地 C++ 對象與 Python 對象(作為 Director 對象)混合在一起,但人們應該意識到一個明顯的事實,即對 Python 對象的方法調用比對 C++ 對象的調用要慢得多。通過僅針對可能在 Python 中擴充的那些方法有選擇地啟用 Director 方法(使用
指令),可以優化這種情況。
%feature
Typemaps for input and output of most of the basic types from director classes have been written. These are roughly the reverse of the usual input and output typemaps used by the wrapper code. The typemap operation names are 'directorin', 'directorout', and 'directorargout'. The director code does not currently use any of the other kinds of typemaps. It is not clear at this point which kinds are appropriate and need to be supported.
已經編寫了 Director 類的大多數基本類型的輸入和輸出的類型映射。這些大緻與包裝器代碼使用的正常輸入和輸出類型映射相反。類型映射操作名稱為
directorin
directorout
。Director 代碼目前不使用任何其他類型的類型映射。目前尚不清楚哪種類型合适并需要支援。
directorargout
Director typemaps for STL classes are in place, and hence you should be able to use std::vector, std::string, etc., as you would any other type.
Note: The director typemaps for return types based in const references, such as
STL 類的 Director 類型映射已經到位,是以你應該能夠像使用其他任何類型一樣使用
std::vector
std::string
等。
注意:Director 類型映射基于 const 引用的傳回類型,例如
class Foo {
...
virtual const int& bar();
...
};
will work only for simple call scenarios. Usually the resulting code is neither thread or reentrant safe. Hence, the user is advised to avoid returning const references in director methods. For example, the user could modify the method interface to use lvalue return types, wherever possible, for example
僅适用于簡單的通話場景。通常,結果代碼既不是線程安全的也不是可重入的。是以,建議使用者避免在 director 方法中傳回 const 引用。例如,使用者可以在可能的情況下修改方法接口以使用左值傳回類型,例如
class Foo {
...
virtual int bar();
...
};
If that is not possible, the user should avoid enabling the director feature for reentrant, recursive or threaded member methods that return const references.
如果不可能,則使用者應避免為傳回 const 引用的可重入、遞歸或線程成員方法啟用 Director 功能。
The last section presented the absolute basics of C/C++ wrapping. If you do nothing but feed SWIG a header file, you will get an interface that mimics the behavior described. However, sometimes this isn't enough to produce a nice module. Certain types of functionality might be missing or the interface to certain functions might be awkward. This section describes some common SWIG features that are used to improve your the interface to an extension module.
最後一部分介紹了 C/C++ 包裝的絕對基礎。如果除了向 SWIG 傳遞頭檔案外什麼也不做,你将獲得一個模仿所描述行為的接口。但是,有時這還不足以産生一個不錯的子產品。某些類型的功能可能會丢失,或者某些功能的接口可能會很尴尬。本節介紹了一些常用的 SWIG 功能,這些功能用于改善擴充子產品的接口。
Sometimes when you create a module, it is missing certain bits of functionality. For example, if you had a function like this
有時,當你建立子產品時,它會缺少某些功能。例如,如果你具有這樣的函數
void set_transform(Image *im, double m[4][4]);
it would be accessible from Python, but there may be no easy way to call it. For example, you might get errors like this:
可以從 Python 通路它,但是可能沒有簡單的方法來調用它。例如,你可能會收到如下錯誤:
>>> a = [
... [1, 0, 0, 0],
... [0, 1, 0, 0],
... [0, 0, 1, 0],
... [0, 0, 0, 1]]
>>> set_transform(im, a)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: Type error. Expected _p_a_4__double
The problem here is that there is no easy way to construct and manipulate a suitable
double [4][4]
value to use. To fix this, you can write some extra C helper functions. Just use the
%inline
這裡的問題是,沒有簡單的方法來構造和操縱要使用的适當的值。要解決此問題,你可以編寫一些額外的 C 輔助函數。隻需使用
double [4][4]
指令即可。例如:
%inline
%inline %{
/* Note: double[4][4] is equivalent to a pointer to an array double (*)[4] */
double (*new_mat44())[4] {
return (double (*)[4]) malloc(16*sizeof(double));
}
void free_mat44(double (*x)[4]) {
free(x);
}
void mat44_set(double x[4][4], int i, int j, double v) {
x[i][j] = v;
}
double mat44_get(double x[4][4], int i, int j) {
return x[i][j];
}
%}
From Python, you could then write code like this:
在 Python 中你可以這樣寫:
>>> a = new_mat44()
>>> mat44_set(a, 0, 0, 1.0)
>>> mat44_set(a, 1, 1, 1.0)
>>> mat44_set(a, 2, 2, 1.0)
...
>>> set_transform(im, a)
>>>
Admittedly, this is not the most elegant looking approach. However, it works and it wasn't too hard to implement. It is possible to clean this up using Python code, typemaps, and other customization features as covered in later sections.
誠然,這不是最優雅的方法。但是,它可以工作,并且實作起來并不難。可以使用 Python 代碼,類型映射和其他自定義功能(如稍後部分所述)進行清理。
If writing support code in C isn't enough, it is also possible to write code in Python. This code gets inserted in to the
.py
file created by SWIG . One use of Python code might be to supply a high-level interface to certain functions. For example:
如果用 C 語言編寫支援代碼還不夠,那麼也可以用 Python 編寫代碼。這段代碼被插入到 SWIG 建立的 .py
檔案中。Python 代碼的一種用途可能是為某些函數提供進階接口。例如:
void set_transform(Image *im, double x[4][4]);
...
/* Rewrite the high level interface to set_transform */
%pythoncode %{
def set_transform(im, x):
a = new_mat44()
for i in range(4):
for j in range(4):
mat44_set(a, i, j, x[i][j])
_example.set_transform(im, a)
free_mat44(a)
%}
In this example,
set_transform()
provides a high-level Python interface built on top of low-level helper functions. For example, this code now seems to work:
在這個例子中, set_transform()
提供了一個基于低級輔助函數的進階 Python 接口。例如,此代碼現在似乎可以工作:
>>> a = [
... [1, 0, 0, 0],
... [0, 1, 0, 0],
... [0, 0, 1, 0],
... [0, 0, 0, 1]]
>>> set_transform(im, a)
>>>
Admittedly, this whole scheme for wrapping the two-dimension array argument is rather ad-hoc. Besides, shouldn't a Python list or a Numeric Python array just work normally? We'll get to those examples soon enough. For now, think of this example as an illustration of what can be done without having to rely on any of the more advanced customization features.
There is also
%pythonbegin
which is another directive very similar to
%pythoncode
, but generates the given Python code at the beginning of the
.py
file. This directive works in the same way as
%pythoncode
, except the code is copied just after the SWIG banner (comment) at the top of the file, before any real code. This provides an opportunity to add your own description in a comment near the top of the file as well as Python imports that have to appear at the top of the file, such as
from __future__ import
statements.
The following shows example usage for Python 2.6 to use
print
as it can in Python 3, that is, as a function instead of a statement:
誠然,用于包裝二維數組參數的整個方案都是臨時的。此外,Python 清單或數字 Python 數組不應該正常工作嗎?我們将盡快了解這些示例。現在,請以該示例為例,說明無需依賴任何更進階的自定義功能即可進行的操作。
還有
,它是另一個類似于
%pythonbegin
的指令,但是在
%pythoncode
檔案的開頭生成給定的 Python 代碼。該指令的工作方式與
.py
相同,不同之處在于,該代碼僅在檔案頂部的 SWIG 智語(注釋)之後,任何實際代碼之前被複制。這提供了一個機會,可以在檔案頂部附近的注釋中添加你自己的描述,以及必須出現在檔案頂部的 Python 導入,例如
%pythoncode
語句。
from __future__ import
下面顯示了 Python 2.6 在 Python 3 中盡可能使用
print
的示例用法,即作為函數而不是語句:
%pythonbegin %{
# This module provides wrappers to the Whizz Bang library
%}
%pythonbegin %{
from __future__ import print_function
print("Loading", "Whizz", "Bang", sep=' ... ')
%}
which can be seen when viewing the first few lines of the generated
.py
file:
檢視生成 .py
檔案的頭幾行将看到:
# This file was automatically generated by SWIG (http://www.SWIG.org).
# Version 2.0.11
#
# Do not make changes to this file unless you know what you are doing--modify
# the SWIG interface file instead.
# This module provides wrappers to the Whizz Bang library
from __future__ import print_function
print("Loading", "Whizz", "Bang", sep=' ... ')
When using
%pythoncode
%pythonbegin
you generally want to make sure that the block is delimited by
%{
%}
. If you delimit it with
{
}
then any lines with a leading
#
will be handled by SWIG as preprocessor directives, when you probably meant them as Python comments. Prior to SWIG 3.0.3, invalid preprocessor directives were silently ignored, so generally using the wrong delimiters resulted in such comments not appearing in the generated output (though a comment starting with a valid preprocessor directive could cause problems, for example:
# error handling
). SWIG 3.0.3 and later report an error for invalid preprocessor directives, so you may have to update existing interface files to delimit blocks of Python code correctly.
As an alternative to providing a block containing Python code, you can include python code from a file. The code is inserted exactly as in the file, so this avoids any issues with the SWIG preprocessor. It's a good approach if you have a non-trivial chunk of Python code to insert. To use this feature you specify a filename in double quotes, for example:
%pythoncode
時,通常需要確定該塊由
%pythonbegin
%{
分隔。如果用
%}
{
分隔行,那麼帶有
}
的任何行都将被 SWIG 作為預處理器指令處理,這時你可能會将它們視為 Python 注釋。在 SWIG 3.0.3 之前,無效的預處理器指令會被靜默忽略,是以通常使用錯誤的定界符會導緻此類注釋未出現在生成的輸出中(盡管以有效預處理器指令開頭的注釋可能會引起問題,例如:
#
# error handling
)。SWIG 3.0.3 和更高版本報告無效的預處理程式指令錯誤,是以你可能必須更新現有的接口檔案以正确地分隔 Python 代碼塊。
作為提供包含 Python 代碼的塊的替代方法,你可以包括檔案中的 Python 代碼。代碼将完全按照檔案中的順序插入,是以可以避免 SWIG 預處理程式出現任何問題。如果你要插入大量的 Python 代碼,這是一個很好的方法。要使用此功能,請在雙引号中指定檔案名,例如:
%pythoncode "somecode.py"
Sometimes you may want to replace or modify the wrapper function that SWIG creates in the proxy
.py
file. The Python module in SWIG provides some features that enable you to do this. First, to entirely replace a proxy function you can use
%feature("shadow")
. For example:
有時你可能想替換或修改 SWIG 在代理檔案中建立的包裝函數。SWIG 中的 Python 子產品提供了一些使你可以執行此操作的功能。首先,要完全替換代理功能,可以使用
.py
%feature("shadow")
%module example
// Rewrite bar() python code
%feature("shadow") Foo::bar(int) %{
def bar(*args):
#do something before
$action
#do something after
%}
class Foo {
public:
int bar(int x);
};
where
$action
will be replaced by the call to the C/C++ proper method.
Often the proxy function created by SWIG is fine, but you simply want to add code to it without touching the rest of the generated function body. For these cases SWIG provides the
pythonprepend
pythonappend
features which do exactly as their names suggest. The
pythonprepend
feature will insert its value at the beginning of the proxy function, and
pythonappend
will insert code at the end of the proxy, just before the return statement.
其中的
$action
将被對 C/C++ 适當方法的調用所代替。
通常,SWIG 建立的代理功能很好,但是你隻想向其中添加代碼,而無需接觸其餘的生成的函數主體。對于這些情況,SWIG 提供了
pythonprepend
功能,它們的功能與名稱一樣。
pythonappend
功能将其值插入代理函數的開頭,而
pythonprepend
将代碼插入代理的末尾,即
pythonappend
語句之前。
return
%module example
// Add python code to bar()
%feature("pythonprepend") Foo::bar(int) %{
#do something before C++ call
%}
%feature("pythonappend") Foo::bar(int) %{
#do something after C++ call
%}
class Foo {
public:
int bar(int x);
};
Notes: Usually the
pythonappend
pythonprepend
features are safer to use than the
shadow
feature. Also, from SWIG version 1.3.28 you can use the directive forms
%pythonappend
%pythonprepend
as follows:
注意:通常,
pythonappend
功能比
pythonprepend
功能更安全使用。另外,從 SWIG 版本 1.3.28 開始,你可以使用指令形式
shadow
%pythonappend
%pythonprepend
%module example
// Add python code to bar()
%pythonprepend Foo::bar(int) %{
#do something before C++ call
%}
%pythonappend Foo::bar(int) %{
#do something after C++ call
%}
class Foo {
public:
int bar(int x);
};
Note that when the underlying C++ method is overloaded, there is only one proxy Python method for multiple C++ methods. In this case, only one of parsed methods is examined for the feature. You are better off specifying the feature without the argument list to ensure it will get used, as it will then get attached to all the overloaded C++ methods. For example:
請注意,當底層 C++ 方法重載時,多個 C++ 方法隻有一個代理 Python 方法。在這種情況下,僅檢查一種解析方法的功能。最好不要在沒有確定可用參數清單的情況下指定該功能,因為它随後将附加到所有重載的 C++ 方法中。例如:
%module example
// Add python code to bar()
%pythonprepend Foo::bar %{
#do something before C++ call
%}
%pythonappend Foo::bar %{
#do something after C++ call
%}
class Foo {
public:
int bar(int x);
int bar();
};
The same applies for overloaded constructors.
對重載構造函數亦然。
36.6.3 用 %extend
擴充類
%extend
One of the more interesting features of SWIG is that it can extend structures and classes with new methods--at least in the Python interface. Here is a simple example:
SWIG 的更有趣的功能之一是它可以使用新方法擴充結構體和類——至少在 Python 接口中。這是一個簡單的示例:
%module example
%{
#include "someheader.h"
%}
struct Vector {
double x, y, z;
};
%extend Vector {
char *__str__() {
static char tmp[1024];
sprintf(tmp, "Vector(%g, %g, %g)", $self->x, $self->y, $self->z);
return tmp;
}
Vector(double x, double y, double z) {
Vector *v = (Vector *) malloc(sizeof(Vector));
v->x = x;
v->y = y;
v->z = z;
return v;
}
};
Now, in Python
現在,在 Python 中
>>> v = example.Vector(2, 3, 4)
>>> print v
Vector(2, 3, 4)
>>>
%extend
can be used for many more tasks than this. For example, if you wanted to overload a Python operator, you might do this:
%extend
可以用于更多任務。例如,如果要重載 Python 運算符,則可以這樣做:
%extend Vector {
Vector __add__(Vector *other) {
Vector v;
v.x = $self->x + other->x;
v.y = $self->y + other->y;
v.z = $self->z + other->z;
return v;
}
};
Use it like this:
這樣使用:
>>> import example
>>> v = example.Vector(2, 3, 4)
>>> w = example.Vector(10, 11, 12)
>>> print v+w
Vector(12, 14, 16)
>>>
%extend
works with both C and C++ code. It does not modify the underlying object in any way--the extensions only show up in the Python interface.
%extend
可以與 C 和 C++ 代碼一起使用。它不會以任何方式修改底層對象——擴充僅顯示在 Python 接口中。
36.6.4 用 %exception
處理異常
%exception
If a C or C++ function throws an error, you may want to convert that error into a Python exception. To do this, you can use the
%exception
directive.
%exception
simply lets you rewrite part of the generated wrapper code to include an error check.
In C, a function often indicates an error by returning a status code (a negative number or a NULL pointer perhaps). Here is a simple example of how you might handle that:
如果 C 或 C++ 函數引發錯誤,則可能需要将該錯誤轉換為 Python 異常。為此,你可以使用
%exception
%exception
僅允許你重寫部分生成的包裝器代碼以包含錯誤檢查。
在 C 語言中,函數通常通過傳回狀态碼(可能為負數或 NULL 指針)來訓示錯誤。這是你可能如何處理的一個簡單示例:
%exception malloc {
$action
if (!result) {
PyErr_SetString(PyExc_MemoryError, "Not enough memory");
SWIG_fail;
}
}
void *malloc(size_t nbytes);
In Python,
在 Python 中,
>>> a = example.malloc(2000000000)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
MemoryError: Not enough memory
>>>
If a library provides some kind of general error handling framework, you can also use that. For example:
如果庫提供某種正常的錯誤處理架構,你也可以使用它。例如:
%exception {
$action
if (err_occurred()) {
PyErr_SetString(PyExc_RuntimeError, err_message());
SWIG_fail;
}
}
No declaration name is given to
%exception
, it is applied to all wrapper functions.
C++ exceptions are also easy to handle. For example, you can write code like this:
沒有為
%exception
指定任何聲明名稱,它适用于所有包裝函數。
C++ 異常也易于處理。例如,你可以編寫如下代碼:
%exception getitem {
try {
$action
} catch (std::out_of_range &e) {
PyErr_SetString(PyExc_IndexError, const_cast<char*>(e.what()));
SWIG_fail;
}
}
class Base {
public:
Foo *getitem(int index); // Exception handled added
...
};
When raising a Python exception from C, use the
PyErr_SetString()
function as shown above followed by
SWIG_fail
. The following exception types can be used as the first argument.
從 C 引發 Python 異常時,請使用如上所示的函數,然後是
PyErr_SetString()
。以下異常類型可以用作第一個參數。
SWIG_fail
PyExc_ArithmeticError
PyExc_AssertionError
PyExc_AttributeError
PyExc_EnvironmentError
PyExc_EOFError
PyExc_Exception
PyExc_FloatingPointError
PyExc_ImportError
PyExc_IndexError
PyExc_IOError
PyExc_KeyError
PyExc_KeyboardInterrupt
PyExc_LookupError
PyExc_MemoryError
PyExc_NameError
PyExc_NotImplementedError
PyExc_OSError
PyExc_OverflowError
PyExc_RuntimeError
PyExc_StandardError
PyExc_SyntaxError
PyExc_SystemError
PyExc_TypeError
PyExc_UnicodeError
PyExc_ValueError
PyExc_ZeroDivisionError
SWIG_fail
is a C macro which when called within the context of SWIG wrapper function, will jump to the error handler code. This will call any cleanup code (freeing any temp variables) and then return from the wrapper function so that the Python interpreter can raise the Python exception. This macro should always be called after setting a Python error in code snippets, such as typemaps and
%exception
, that are ultimately generated into the wrapper function.
The language-independent
exception.i
library file can also be used to raise exceptions. See the SWIG Library chapter.
是一個 C 宏,當在 SWIG 包裝函數的上下文中調用時,将跳轉到錯誤處理程式代碼。這将調用任何清除代碼(釋放所有臨時變量),然後從包裝器函數傳回,以便 Python 解釋器可以引發 Python 異常。在代碼片段中設定 Python 錯誤(例如類型映射和
SWIG_fail
%exception
)後,應始終調用該宏,這些錯誤最終會生成到包裝函數中。
獨立于語言的
庫檔案也可以用于引發異常。請參閱 SWIG 庫一章。
exception.i
Although SWIG is largely automatic, there are certain types of wrapping problems that require additional user input. Examples include dealing with output parameters, strings, binary data, and arrays. This chapter discusses the common techniques for solving these problems.
盡管 SWIG 在很大程度上是自動的,但是某些類型的包裝問題需要額外的使用者輸入。示例包括處理輸出參數、字元串、二進制資料和數組。本章讨論解決這些問題的常用技術。
A common problem in some C programs is handling parameters passed as simple pointers. For example:
在某些 C 程式中,一個常見的問題是處理作為簡單指針傳遞的參數。例如:
void add(int x, int y, int *result) {
*result = x + y;
}
or perhaps
或者
int sub(int *x, int *y) {
return *x-*y;
}
The easiest way to handle these situations is to use the
typemaps.i
file. For example:
處理這些情況的最簡單方法是使用 typemaps.i
檔案。例如:
%module example
%include "typemaps.i"
void add(int, int, int *OUTPUT);
int sub(int *INPUT, int *INPUT);
In Python, this allows you to pass simple values. For example:
在 Python 中,這允許你傳遞簡單的值。例如:
>>> a = add(3, 4)
>>> print a
7
>>> b = sub(7, 4)
>>> print b
3
>>>
Notice how the
INPUT
parameters allow integer values to be passed instead of pointers and how the
OUTPUT
parameter creates a return result.
If you don't want to use the names
INPUT
OUTPUT
, use the
%apply
注意參數如何允許傳遞整數值而不是指針,以及
INPUT
OUTPUT
參數如何建立傳回結果。
如果你不想使用名稱
INPUT
,請使用
OUTPUT
%apply
%module example
%include "typemaps.i"
%apply int *OUTPUT { int *result };
%apply int *INPUT { int *x, int *y};
void add(int x, int y, int *result);
int sub(int *x, int *y);
If a function mutates one of its parameters like this,
如果一個函數像這樣改變它的一個參數,
void negate(int *x) {
*x = -(*x);
}
you can use
INOUT
你可以這樣是用 INOUT
:
%include "typemaps.i"
...
void negate(int *INOUT);
In Python, a mutated parameter shows up as a return value. For example:
在 Python 中,改變的參數顯示為傳回值。例如:
>>> a = negate(3)
>>> print a
-3
>>>
Note: Since most primitive Python objects are immutable, it is not possible to perform in-place modification of a Python object passed as a parameter.
The most common use of these special typemap rules is to handle functions that return more than one value. For example, sometimes a function returns a result as well as a special error code:
注意:由于大多數原始 Python 對象都是不可變的,是以無法對作為參數傳遞的 Python 對象執行就地修改。
這些特殊類型映射規則最常見的用途是處理傳回多個值的函數。例如,有時函數傳回結果以及特殊的錯誤代碼:
/* send message, return number of bytes sent, along with success code */
int send_message(char *text, int len, int *success);
To wrap such a function, simply use the
OUTPUT
rule above. For example:
要包裝這樣的函數,隻需使用上面的 OUTPUT
規則。例如:
%module example
%include "typemaps.i"
%apply int *OUTPUT { int *success };
...
int send_message(char *text, int *success);
When used in Python, the function will return multiple values.
在 Python 中使用時,該函數将傳回多個值。
bytes, success = send_message("Hello World")
if not success:
print "Whoa!"
else:
print "Sent", bytes
Another common use of multiple return values are in query functions. For example:
多個傳回值的另一個常見用法是查詢函數。例如:
void get_dimensions(Matrix *m, int *rows, int *columns);
To wrap this, you might use the following:
要包裝這樣的函數,你可以這樣做:
%module example
%include "typemaps.i"
%apply int *OUTPUT { int *rows, int *columns };
...
void get_dimensions(Matrix *m, int *rows, *columns);
Now, in Python:
現在,在 Python 中:
>>> r, c = get_dimensions(m)
Be aware that the primary purpose of the
typemaps.i
file is to support primitive datatypes. Writing a function like this
請注意, typemaps.i
檔案的主要目的是支援原始資料類型。編寫這樣的函數
void foo(Bar *OUTPUT);
may not have the intended effect since
typemaps.i
does not define an OUTPUT rule for
Bar
可能沒有預期的效果,因為
typemaps.i
定義
Bar
規則。
OUTPUT
If you must work with simple pointers such as
int *
double *
and you don't want to use
typemaps.i
, consider using the
cpointer.i
library file. For example:
如果你必須使用諸如
int *
之類的簡單指針,并且不想使用
double *
,請考慮使用
typemaps.i
庫檔案。例如:
cpointer.i
%module example
%include "cpointer.i"
%inline %{
extern void add(int x, int y, int *result);
%}
%pointer_functions(int, intp);
%pointer_functions(type, name)
macro generates five helper functions that can be used to create, destroy, copy, assign, and dereference a pointer. In this case, the functions are as follows:
%pointer_functions(type,name)
宏生成五個輔助函數,可用于建立、銷毀、複制、配置設定和取消引用指針。在這種情況下,函數如下:
int *new_intp();
int *copy_intp(int *x);
void delete_intp(int *x);
void intp_assign(int *x, int value);
int intp_value(int *x);
In Python, you would use the functions like this:
在 Python 中你可以這樣使用這些函數:
>>> result = new_intp()
>>> print result
_108fea8_p_int
>>> add(3, 4, result)
>>> print intp_value(result)
7
>>>
If you replace
%pointer_functions()
by
%pointer_class(type, name)
, the interface is more class-like.
如果你用替換
%pointer_class(type, name)
,接口會更像類。
%pointer_functions()
>>> result = intp()
>>> add(3, 4, result)
>>> print result.value()
7
See the SWIG Library chapter for further details.
更多細節參見 SWIG 庫 章節。
Sometimes a C function expects an array to be passed as a pointer. For example,
有時,C 函數希望将數組作為指針傳遞。例如,
int sumitems(int *first, int nitems) {
int i, sum = 0;
for (i = 0; i < nitems; i++) {
sum += first[i];
}
return sum;
}
To wrap this into Python, you need to pass an array pointer as the first argument. A simple way to do this is to use the
carrays.i
要将其包裝到 Python 中,你需要傳遞一個數組指針作為第一個參數。一種簡單的方法是使用 carrays.i
%include "carrays.i"
%array_class(int, intArray);
%array_class(type, name)
macro creates wrappers for an unbounded array object that can be passed around as a simple pointer like
int *
double *
. For instance, you will be able to do this in Python:
宏為無邊界數組對象建立包裝器,該包裝器可以作為簡單指針如
%array_class(type, name)
int *
傳遞。例如,你将能夠在 Python 中執行此操作:
double *
>>> a = intArray(10000000) # Array of 10-million integers
>>> for i in xrange(10000): # Set some values
... a[i] = i
>>> sumitems(a, 10000)
49995000
>>>
The array "object" created by
%array_class()
does not encapsulate pointers inside a special array object. In fact, there is no bounds checking or safety of any kind (just like in C). Because of this, the arrays created by this library are extremely low-level indeed. You can't iterate over them nor can you even query their length. In fact, any valid memory address can be accessed if you want (negative indices, indices beyond the end of the array, etc.). Needless to say, this approach is not going to suit all applications. On the other hand, this low-level approach is extremely efficient and well suited for applications in which you need to create buffers, package binary data, etc.
由 %array_class()
建立的數組“對象”沒有将指針封裝在特殊的數組對象中。實際上,沒有邊界檢查或任何類型的安全性(就像C中一樣)。是以,此庫建立的數組确實是非常低級的。你無法周遊它們,甚至無法查詢它們的長度。實際上,如果需要,可以通路任何有效的記憶體位址(負索引,超出數組末尾的索引等)。不用說,這種方法并不适合所有應用程式。另一方面,這種低級方法非常高效,非常适合需要建立緩沖區,打包二進制資料等的應用程式。
If a C function has an argument of
char *
, then a Python string can be passed as input. For example:
如果 C 函數的參數為 char *
,則可以将 Python 字元串作為輸入傳遞。例如:
// C
void foo(char *s);
# Python
>>> foo("Hello")
When a Python string is passed as a parameter, the C function receives a pointer to the raw data contained in the string. Since Python strings are immutable, it is illegal for your program to change the value. In fact, doing so will probably crash the Python interpreter.
If your program modifies the input parameter or uses it to return data, consider using the
cstring.i
library file described in the SWIG Library chapter.
When functions return a
char *
, it is assumed to be a NULL-terminated string. Data is copied into a new Python string and returned.
If your program needs to work with binary data, you can use a typemap to expand a Python string into a pointer/length argument pair. As luck would have it, just such a typemap is already defined. Just do this:
當将 Python 字元串作為參數傳遞時,C 函數将收到一個指向該字元串中包含的原始資料的指針。由于 Python 字元串是不可變的,是以程式更改該值是非法的。實際上,這樣做可能會使 Python 解釋器崩潰。
如果你的程式修改了輸入參數或使用它來傳回資料,請考慮使用 SWIG 庫一章。
當函數傳回一個
char *
時,它被認為是一個以 NULL 結尾的字元串。資料被複制到新的 Python 字元串中并傳回。
如果你的程式需要使用二進制資料,則可以使用類型映射将 Python 字元串擴充為指針/長度參數對。幸運的是,已經定義了這種類型映射。隻要這樣做:
%apply (char *STRING, int LENGTH) { (char *data, int size) };
...
int parity(char *data, int size, int initial);
Now in Python:
現在 Python 中:
>>> parity("e\x09ffss\x00\x00\x01\nx", 0)
If you need to return binary data, you might use the
cstring.i
library file. The
cdata.i
library can also be used to extra binary data from arbitrary pointers.
如果需要傳回二進制資料,可以使用庫檔案。
cstring.i
庫也可以用于從任意指針中擷取額外的二進制資料。
cdata.i
C++ default argument code generation is documented in the main Default argumentssection. There is also an optional Python specific feature that can be used called the
python:cdefaultargs
feature flag. By default, SWIG attempts to convert C++ default argument values into Python values and generates code into the Python layer containing these values. For example:
C++ 預設參數代碼的生成主要記錄在預設參數部分中。還有一個可選的 Python 特定功能,稱為 python:cdefaultargs
功能标志。預設情況下,SWIG 嘗試将 C++ 預設參數值轉換為 Python 值,并在包含這些值的 Python 層中生成代碼。例如:
struct CDA {
int fff(int a = 1, bool b = false);
};
From Python this can be called as follows:
Python 中可以這樣調用:
>>> CDA().fff() # C++ layer receives a=1 and b=false
>>> CDA().fff(2) # C++ layer receives a=2 and b=false
>>> CDA().fff(3, True) # C++ layer receives a=3 and b=true
The default code generation in the Python layer is:
Python 層中的預設參數生成代碼如下:
class CDA(object):
...
def fff(self, a=1, b=False):
return _default_args.CDA_fff(self, a, b)
Adding the feature:
添加功能:
%feature("python:cdefaultargs") CDA::fff;
struct CDA {
int fff(int a = 1, bool b = false);
results in identical behaviour when called from Python, however, it results in different code generation:
從 Python 調用時會導緻相同的行為,但是會導緻生成不同的代碼:
class CDA(object):
...
def fff(self, *args):
return _default_args.CDA_fff(self, *args)
The default arguments are obtained in the C++ wrapper layer instead of the Python layer. Some code generation modes are quite different, eg
-builtin
-fastproxy
, and are unaffected by
python:cdefaultargs
as the default values are always obtained from the C++ layer.
Note that not all default arguments can be converted into a Python equivalent. When SWIG does not convert them, it will generate code to obtain them from the C++ layer as if
python:cdefaultargs
was specified. This will happen if just one argument cannot be converted into a Python equivalent. This occurs typically when the argument is not fully numeric, such as
int(1)
預設參數在 C++ 包裝器層而不是 Python 層中獲得。某些代碼生成模式大不相同,例如
-builtin
,并且不受
-fastproxy
python:cdefaultargs
的影響,因為預設值始終是從 C++ 層獲得的。
請注意,并非所有預設參數都可以轉換為等效的 Python 實作。如果 SWIG 不轉換它們,它将生成代碼以從 C++ 層擷取它們,就像指定了
一樣。如果僅一個參數不能轉換為等效的 Python 就會發生這種情況。通常在參數不是全數字的情況下發生,例如
python:cdefaultargs
int(1)
struct CDA {
int fff(int a = int(1), bool b = false);
};
Compatibility Note: SWIG -3.0.6 introduced the
python:cdefaultargs
feature. Versions of SWIG prior to this varied in their ability to convert C++ default values into equivalent Python default argument values.
相容性說明: SWIG -3.0.6 引入了 python:cdefaultargs
功能。在此之前,SWIG 的版本将 C++ 預設值轉換為等效的 Python 預設參數值的能力各不相同。
This section describes how you can modify SWIG's default wrapping behavior for various C/C++ datatypes using the
%typemap
directive. This is an advanced topic that assumes familiarity with the Python C API as well as the material in the "Typemaps" chapter.
Before proceeding, it should be stressed that typemaps are not a required part of using SWIG ---the default wrapping behavior is enough in most cases. Typemaps are only used if you want to change some aspect of the primitive C-Python interface or if you want to elevate your guru status.
本節描述了如何使用
%typemap
指令修改 SWIG 對各種 C/C++ 資料類型的預設包裝行為。這是一個進階主題,假定你熟悉 Python C API 以及類型映射一章中的内容。
在繼續之前,應該強調的是類型映射不是使用 SWIG 的必需部分——在大多數情況下,預設的包裝行為就足夠了。隻有當你要更改原始 C-Python 接口的某些方面或要成為專家時,才使用類型映射。
A typemap is nothing more than a code generation rule that is attached to a specific C datatype. For example, to convert integers from Python to C, you might define a typemap like this:
類型映射不過是附加到特定 C 資料類型的代碼生成規則。例如,要将整數從 Python 轉換為 C,你可以定義如下類型映射:
%module example
%typemap(in) int {
$1 = (int) PyLong_AsLong($input);
printf("Received an integer : %d\n", $1);
}
%inline %{
extern int fact(int n);
%}
Typemaps are always associated with some specific aspect of code generation. In this case, the "in" method refers to the conversion of input arguments to C/C++. The datatype
int
is the datatype to which the typemap will be applied. The supplied C code is used to convert values. In this code a number of special variable prefaced by a
$
are used. The
$1
variable is placeholder for a local variable of type
int
. The
$input
variable is the input object of type
PyObject *
When this example is compiled into a Python module, it operates as follows:
類型映射始終與代碼生成的某些特定方面相關聯。在這種情況下,方法是指将輸入參數轉換為 C/C++ 。資料類型
in
是将要應用類型映射的資料類型。提供的 C 代碼用于轉換值。在此代碼中,使用了多個以
int
開頭的特殊變量。
$
變量是類型為
$1
的局部變量的占位符。
int
$input
PyObject *
的輸入對象。
将此示例編譯為 Python 子產品時,其操作如下:
>>> from example import *
>>> fact(6)
Received an integer : 6
720
In this example, the typemap is applied to all occurrences of the
int
datatype. You can refine this by supplying an optional parameter name. For example:
在此示例中,類型映射應用于所有出現的 int
資料類型。你可以通過提供可選的參數名稱來優化此功能。例如:
%module example
%typemap(in) int nonnegative {
$1 = (int) PyLong_AsLong($input);
if ($1 < 0) {
PyErr_SetString(PyExc_ValueError, "Expected a nonnegative value.");
SWIG_fail;
}
}
%inline %{
extern int fact(int nonnegative);
%}
In this case, the typemap code is only attached to arguments that exactly match
int nonnegative
The application of a typemap to specific datatypes and argument names involves more than simple text-matching--typemaps are fully integrated into the SWIG C++ type-system. When you define a typemap for
int
, that typemap applies to
int
and qualified variations such as
const int
. In addition, the typemap system follows
typedef
declarations. For example:
在這種情況下,類型映射代碼僅附加到與
int nonnegative
完全比對的參數上。
将類型映射應用于特定的資料類型和參數名稱所涉及的不僅僅是簡單的文本比對,類型映射已完全內建到 SWIG C++ 類型系統中。當你為
定義類型映射時,該類型映射适用于
int
和限定變量,例如
int
。另外,類型映射系統遵循
const int
聲明。例如:
typedef
%typemap(in) int n {
$1 = (int) PyLong_AsLong($input);
printf("n = %d\n", $1);
}
%inline %{
typedef int Integer;
extern int fact(Integer n); // Above typemap is applied
%}
Typemaps can also be defined for groups of consecutive arguments. For example:
還可以為連續參數組定義類型映射。例如:
%typemap(in) (char *str, int len) {
$1 = PyString_AsString($input);
$2 = PyString_Size($input);
};
int count(char c, char *str, int len);
When a multi-argument typemap is defined, the arguments are always handled as a single Python object. This allows the function to be used like this (notice how the length parameter is omitted):
定義多參數類型映射時,參數始終作為單個 Python 對象處理。這允許像下面這樣使用該函數(注意如何省略長度參數):
>>> example.count('e', 'Hello World')
1
>>>
The previous section illustrated an "in" typemap for converting Python objects to C. A variety of different typemap methods are defined by the Python module. For example, to convert a C integer back into a Python object, you might define an "out" typemap like this:
上一節說明了用于将 Python 對象轉換為 C 的類型映射。Python 子產品定義了各種不同的類型映射方法。例如,要将 C 整數轉換回 Python 對象,可以定義一個
in
類型映射,如下所示:
out
%typemap(out) int {
$result = PyInt_FromLong((long) $1);
}
A detailed list of available methods can be found in the "Typemaps" chapter.
However, the best source of typemap information (and examples) is probably the Python module itself. In fact, all of SWIG's default type handling is defined by typemaps. You can view these typemaps by looking at the files in the SWIG library. Just take into account that in the latest versions of SWIG (1.3.22+), the library files are not very pristine clear for the casual reader, as they used to be. The extensive use of macros and other ugly techniques in the latest version produce a very powerful and consistent python typemap library, but at the cost of simplicity and pedagogic value.
To learn how to write a simple or your first typemap, you better take a look at the SWIG library version 1.3.20 or so.
可用方法的詳細清單可以在類型映射一章中找到。
但是,類型映射資訊(和示例)的最佳來源可能是 Python 子產品本身。實際上,SWIG 的所有預設類型處理都是由類型映射定義的。你可以通過檢視 SWIG 庫中的檔案來檢視這些類型映射。隻需考慮到在最新版本的 SWIG(1.3.22+)中,庫檔案對于臨時閱讀者來說就不是很原始了。最新版本中大量使用宏和其他難看的技術産生了一個非常強大且一緻的 Python typemap 庫,但是卻以簡單性和教學價值為代價。
要學習如何編寫簡單的,或你的第一個類型映射,最好檢視版本 1.3.20 左右的 SWIG 庫。
Within typemap code, a number of special variables prefaced with a
$
may appear. A full list of variables can be found in the "Typemaps" chapter. This is a list of the most common variables:
在類型映射代碼中,可能會出現許多以 $
開頭的特殊變量。變量的完整清單可以在類型映射一章中找到。這是最常見的變量的清單:
$1
A C local variable corresponding to the actual type specified in the
%typemap
directive. For input values, this is a C local variable that's supposed to hold an argument value. For output values, this is the raw result that's supposed to be returned to Python.
一個 C 局部變量,對應于 %typemap
指令中指定的實際類型。對于輸入值,這是 C 局部變量,應該儲存參數值。對于輸出值,這是應該傳回給 Python 的原始結果。
$input
A
PyObject *
holding a raw Python object with an argument or variable value.
PyObject *
持有參數或變量值的原始 Python 對象。
$result
PyObject *
that holds the result to be returned to Python.
PyObject *
持有 Python 傳回的結果。
$1_name
The parameter name that was matched.
參數名被比對。
$1_type
The actual C datatype matched by the typemap.
C 資料類型有類型映射比對。
$1_ltype
An assignable version of the datatype matched by the typemap (a type that can appear on the left-hand-side of a C assignment operation). This type is stripped of qualifiers and may be an altered version of
$1_type
. All arguments and local variables in wrapper functions are declared using this type so that their values can be properly assigned.
與類型映射比對的資料類型的可配置設定版本(一種類型,可以顯示在 C 配置設定操作的左側)。這個類型沒有限定符,可以是 $1_type
的修改版本。包裝函數中的所有參數和局部變量都使用此類型聲明,以便可以正确配置設定它們的值。
$symname
The Python name of the wrapper function being created.
包裝函數在 Python 中的名字被建立。
When you write a typemap, you usually have to work directly with Python objects. The following functions may prove to be useful.
編寫類型映射時,通常必須直接使用 Python 對象。以下函數可能被證明是有用的。
Python Integer Functions
PyObject *PyInt_FromLong(long l);
long PyInt_AsLong(PyObject *);
int PyInt_Check(PyObject *);
Python Floating Point Functions
PyObject *PyFloat_FromDouble(double);
double PyFloat_AsDouble(PyObject *);
int PyFloat_Check(PyObject *);
Python String Functions
PyObject *PyString_FromString(char *);
PyObject *PyString_FromStringAndSize(char *, lint len);
int PyString_Size(PyObject *);
char *PyString_AsString(PyObject *);
int PyString_Check(PyObject *);
Python List Functions
PyObject *PyList_New(int size);
int PyList_Size(PyObject *list);
PyObject *PyList_GetItem(PyObject *list, int i);
int PyList_SetItem(PyObject *list, int i, PyObject *item);
int PyList_Insert(PyObject *list, int i, PyObject *item);
int PyList_Append(PyObject *list, PyObject *item);
PyObject *PyList_GetSlice(PyObject *list, int i, int j);
int PyList_SetSlice(PyObject *list, int i, int , PyObject *list2);
int PyList_Sort(PyObject *list);
int PyList_Reverse(PyObject *list);
PyObject *PyList_AsTuple(PyObject *list);
int PyList_Check(PyObject *);
Python Tuple Functions
PyObject *PyTuple_New(int size);
int PyTuple_Size(PyObject *);
PyObject *PyTuple_GetItem(PyObject *, int i);
int PyTuple_SetItem(PyObject *, int i, PyObject *item);
PyObject *PyTuple_GetSlice(PyObject *t, int i, int j);
int PyTuple_Check(PyObject *);
Python Dictionary Functions
PyObject *PyDict_New();
int PyDict_Check(PyObject *);
int PyDict_SetItem(PyObject *p, PyObject *key, PyObject *val);
int PyDict_SetItemString(PyObject *p, const char *key, PyObject *val);
int PyDict_DelItem(PyObject *p, PyObject *key);
int PyDict_DelItemString(PyObject *p, char *key);
PyObject* PyDict_Keys(PyObject *p);
PyObject* PyDict_Values(PyObject *p);
PyObject* PyDict_GetItem(PyObject *p, PyObject *key);
PyObject* PyDict_GetItemString(PyObject *p, const char *key);
int PyDict_Next(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue);
Py_ssize_t PyDict_Size(PyObject *p);
int PyDict_Update(PyObject *a, PyObject *b);
int PyDict_Merge(PyObject *a, PyObject *b, int override);
PyObject* PyDict_Items(PyObject *p);
Python File Conversion Functions
PyObject *PyFile_FromFile(FILE *f);
FILE *PyFile_AsFile(PyObject *);
int PyFile_Check(PyObject *);
Abstract Object Interface
write me
This section includes a few examples of typemaps. For more examples, you might look at the files
python.swg
typemaps.i
本節包含一些類型映射的示例。有關更多示例,你可以檢視 SWIG 庫中的檔案
python.swg
typemaps.i
36.9.1 将 Python list 轉換為 char **
char **
A common problem in many C programs is the processing of command line arguments, which are usually passed in an array of NULL terminated strings. The following SWIG interface file allows a Python list object to be used as a
char **
object.
許多 C 程式中的一個常見問題是指令行參數的處理,這些參數通常以 NULL 終止的字元串數組形式傳遞。以下 SWIG 接口檔案允許将 Python 清單對象用作 char **
對象。
%module argv
// This tells SWIG to treat char ** as a special case
%typemap(in) char ** {
/* Check if is a list */
if (PyList_Check($input)) {
int size = PyList_Size($input);
int i = 0;
$1 = (char **) malloc((size+1)*sizeof(char *));
for (i = 0; i < size; i++) {
PyObject *o = PyList_GetItem($input, i);
if (PyString_Check(o)) {
$1[i] = PyString_AsString(PyList_GetItem($input, i));
} else {
free($1);
PyErr_SetString(PyExc_TypeError, "list must contain strings");
SWIG_fail;
}
}
$1[i] = 0;
} else {
PyErr_SetString(PyExc_TypeError, "not a list");
SWIG_fail;
}
}
// This cleans up the char ** array we malloc'd before the function call
%typemap(freearg) char ** {
free((char *) $1);
}
// Now a test function
%inline %{
int print_args(char **argv) {
int i = 0;
while (argv[i]) {
printf("argv[%d] = %s\n", i, argv[i]);
i++;
}
return i;
}
%}
When this module is compiled, the wrapped C function now operates as follows :
當子產品被編譯後,包裝後的 C 函數這樣操作:
>>> from argv import *
>>> print_args(["Dave", "Mike", "Mary", "Jane", "John"])
argv[0] = Dave
argv[1] = Mike
argv[2] = Mary
argv[3] = Jane
argv[4] = John
5
In the example, two different typemaps are used. The "in" typemap is used to receive an input argument and convert it to a C array. Since dynamic memory allocation is used to allocate memory for the array, the "freearg" typemap is used to later release this memory after the execution of the C function.
在示例中,使用了兩個不同的類型映射。類型映射用于接受輸入參數并将其轉換為 C 數組。由于動态記憶體配置設定用于為數組配置設定記憶體,是以在執行 C 函數之後,将使用
in
類型映射稍後釋放該記憶體。
freearg
Suppose that you had a collection of C functions with arguments such as the following:
假設你有一個帶有以下參數的 C 函數集合:
int foo(int argc, char **argv);
In the previous example, a typemap was written to pass a Python list as the
char **argv
. This allows the function to be used from Python as follows:
在前面的示例中,編寫了一個類型映射以将 Python 清單作為 char **argv
傳遞。這樣可以從 Python 中按以下方式使用該函數:
>>> foo(4, ["foo", "bar", "spam", "1"])
Although this works, it's a little awkward to specify the argument count. To fix this, a multi-argument typemap can be defined. This is not very difficult--you only have to make slight modifications to the previous example:
盡管這可行,但指定參數計數有點尴尬。為了解決這個問題,可以定義一個多參數的類型映射。這不是很困難,你隻需對上一個示例進行一些修改:
%typemap(in) (int argc, char **argv) {
/* Check if is a list */
if (PyList_Check($input)) {
int i;
$1 = PyList_Size($input);
$2 = (char **) malloc(($1+1)*sizeof(char *));
for (i = 0; i < $1; i++) {
PyObject *o = PyList_GetItem($input, i);
if (PyString_Check(o)) {
$2[i] = PyString_AsString(PyList_GetItem($input, i));
} else {
free($2);
PyErr_SetString(PyExc_TypeError, "list must contain strings");
SWIG_fail;
}
}
$2[i] = 0;
} else {
PyErr_SetString(PyExc_TypeError, "not a list");
SWIG_fail;
}
}
%typemap(freearg) (int argc, char **argv) {
free((char *) $2);
}
When writing a multiple-argument typemap, each of the types is referenced by a variable such as
$1
$2
. The typemap code simply fills in the appropriate values from the supplied Python object.
With the above typemap in place, you will find it no longer necessary to supply the argument count. This is automatically set by the typemap code. For example:
編寫多參數類型映射時,每個類型均由變量
$1
$2
引用。類型映射代碼隻是從提供的 Python 對象中填充适當的值。
使用上面的類型映射,你将發現不再需要提供參數計數。這是由類型映射代碼自動設定的。例如:
>>> foo(["foo", "bar", "spam", "1"])
If your function is overloaded in C++, for example:
如果你的函數在 C++ 中沖在,例如:
int foo(int argc, char **argv);
int foo();
don't forget to also provide a suitable typecheck typemap for overloading such as:
不要忘了也為重載類型映射提供一個合适的類型檢查:
%typecheck( SWIG_TYPECHECK_STRING_ARRAY) (int argc, char **argv) {
$1 = PyList_Check($input) ? 1 : 0;
}
If you don't you'll get an error message along the lines of:
如果不提供,你将會得到一條錯誤資訊:
Traceback (most recent call last):
File "runme.py", line 3, in >module<
example.foo(["foo", "bar", "spam", "1"])
NotImplementedError: Wrong number or type of arguments for overloaded function 'foo'.
Possible C/C++ prototypes are:
foo(int, char **)
foo()
A common problem in some C programs is that values may be returned in arguments rather than in the return value of a function. For example:
在某些 C 程式中,一個常見的問題是值可能在參數中傳回,而不是在函數的傳回值中傳回。例如:
/* Returns a status value and two values in out1 and out2 */
int spam(double a, double b, double *out1, double *out2) {
... Do a bunch of stuff ...
*out1 = result1;
*out2 = result2;
return status;
}
A typemap can be used to handle this case as follows :
一個類型映射可以用來處理這種情況,如下所示:
%module outarg
// This tells SWIG to treat an double * argument with name 'OutValue' as
// an output value. We'll append the value to the current result which
// is guaranteed to be a List object by SWIG .
%typemap(argout) double *OutValue {
PyObject *o, *o2, *o3;
o = PyFloat_FromDouble(*$1);
if ((!$result) || ($result == Py_None)) {
$result = o;
} else {
if (!PyTuple_Check($result)) {
PyObject *o2 = $result;
$result = PyTuple_New(1);
PyTuple_SetItem($result, 0, o2);
}
o3 = PyTuple_New(1);
PyTuple_SetItem(o3, 0, o);
o2 = $result;
$result = PySequence_Concat(o2, o3);
Py_DECREF(o2);
Py_DECREF(o3);
}
}
int spam(double a, double b, double *OutValue, double *OutValue);
The typemap works as follows. First, a check is made to see if any previous result exists. If so, it is turned into a tuple and the new output value is concatenated to it. Otherwise, the result is returned normally. For the sample function
spam()
, there are three output values--meaning that the function will return a 3-tuple of the results.
As written, the function must accept 4 arguments as input values, last two being pointers to doubles. If these arguments are only used to hold output values (and have no meaningful input value), an additional typemap can be written. For example:
類型映射的工作方式如下。首先,進行檢查以檢視是否存在任何先前的結果。如果是這樣,它将變成一個元組并将新的輸出值連接配接到它。否則,結果将正常傳回。對于樣本函數
spam()
,有三個輸出值——這意味着該函數将傳回結果的三元組。
按照編寫的方法,該函數必須接受 4 個參數作為輸入值,最後兩個是指向
的指針。如果這些參數僅用于儲存輸出值(并且沒有有意義的輸入值),則可以編寫其他類型映射。例如:
double
%typemap(in, numinputs=0) double *OutValue(double temp) {
$1 = &temp;
}
By specifying numinputs=0, the input value is ignored. However, since the argument still has to be set to some meaningful value before calling C, it is set to point to a local variable
temp
. When the function stores its output value, it will simply be placed in this local variable. As a result, the function can now be used as follows:
通過指定,輸入值将被忽略。但是,由于在調用 C 之前仍必須将參數設定為一些有意義的值,是以将其設定為指向局部變量
numinputs = 0
。當函數存儲其輸出值時,它将簡單地放置在此局部變量中。結果,該函數現在可以按以下方式使用:
temp
>>> a = spam(4, 5)
>>> print a
(0, 2.45, 5.0)
>>> x, y, z = spam(4, 5)
>>>
In some applications, it is sometimes desirable to pass small arrays of numbers as arguments. For example :
在某些應用程式中,有時希望傳遞數字的小數組作為參數。例如:
extern void set_direction(double a[4]); // Set direction vector
This too, can be handled used typemaps as follows :
類型映射也可以用來處理,如下所示:
// Grab a 4 element array as a Python 4-tuple
%typemap(in) double[4](double temp[4]) { // temp[4] becomes a local variable
int i;
if (PyTuple_Check($input)) {
if (!PyArg_ParseTuple($input, "dddd", temp, temp+1, temp+2, temp+3)) {
PyErr_SetString(PyExc_TypeError, "tuple must have 4 elements");
SWIG_fail;
}
$1 = &temp[0];
} else {
PyErr_SetString(PyExc_TypeError, "expected a tuple.");
SWIG_fail;
}
}
This allows our
set_direction
function to be called from Python as follows :
這讓 set_direction
函數在 Python 中可以按以下方式使用:
>>> set_direction((0.5, 0.0, 1.0, -0.25))
Since our mapping copies the contents of a Python tuple into a C array, such an approach would not be recommended for huge arrays, but for small structures, this approach works fine.
由于我們的映射将 Python 元組的内容複制到 C 數組中,是以不建議将這種方法用于大型數組,但對于小型結構體,則此方法效果很好。
Suppose that you wanted to generalize the previous example to handle C arrays of different sizes. To do this, you might write a typemap as follows:
假設你想推廣前面的示例以處理不同大小的 C 數組。為此,你可以按如下方式編寫類型映射:
// Map a Python sequence into any sized C double array
%typemap(in) double[ANY](double temp[$1_dim0]) {
int i;
if (!PySequence_Check($input)) {
PyErr_SetString(PyExc_TypeError, "Expecting a sequence");
SWIG_fail;
}
if (PyObject_Length($input) != $1_dim0) {
PyErr_SetString(PyExc_ValueError, "Expecting a sequence with $1_dim0 elements");
SWIG_fail;
}
for (i =0; i < $1_dim0; i++) {
PyObject *o = PySequence_GetItem($input, i);
if (!PyFloat_Check(o)) {
Py_XDECREF(o);
PyErr_SetString(PyExc_ValueError, "Expecting a sequence of floats");
SWIG_fail;
}
temp[i] = PyFloat_AsDouble(o);
Py_DECREF(o);
}
$1 = &temp[0];
}
In this case, the variable
$1_dim0
is expanded to match the array dimensions actually used in the C code. This allows the typemap to be applied to types such as:
在這種情況下,變量 $1_dim0
被擴充以比對 C 代碼中實際使用的數組尺寸。這允許将類型映射應用于以下類型:
void foo(double x[10]);
void bar(double a[4], double b[8]);
Since the above typemap code gets inserted into every wrapper function where used, it might make sense to use a helper function instead. This will greatly reduce the amount of wrapper code. For example:
由于上面的類型映射代碼被插入到每個包裝函數中使用的位置,是以使用輔助函數可能更有意義。這将大大減少包裝器代碼的數量。例如:
%{
static int convert_darray(PyObject *input, double *ptr, int size) {
int i;
if (!PySequence_Check(input)) {
PyErr_SetString(PyExc_TypeError, "Expecting a sequence");
return 0;
}
if (PyObject_Length(input) != size) {
PyErr_SetString(PyExc_ValueError, "Sequence size mismatch");
return 0;
}
for (i =0; i < size; i++) {
PyObject *o = PySequence_GetItem(input, i);
if (!PyFloat_Check(o)) {
Py_XDECREF(o);
PyErr_SetString(PyExc_ValueError, "Expecting a sequence of floats");
return 0;
}
ptr[i] = PyFloat_AsDouble(o);
Py_DECREF(o);
}
return 1;
}
%}
%typemap(in) double [ANY](double temp[$1_dim0]) {
if (!convert_darray($input, temp, $1_dim0)) {
SWIG_fail;
}
$1 = &temp[0];
}
Occasionally, it might be necessary to convert pointer values that have been stored using the SWIG typed-pointer representation. Since there are several ways in which pointers can be represented, the following two functions are used to safely perform this conversion:
有時,可能有必要轉換使用 SWIG 類型指針表示形式存儲的指針值。由于可以通過多種方式表示指針,是以可以使用以下兩個函數安全地執行此轉換:
int SWIG_ConvertPtr(PyObject *obj, void **ptr, SWIG_type_info *ty, int flags)
Converts a Python object
obj
to a C pointer. The result of the conversion is placed into the pointer located at
ptr
ty
is a SWIG type descriptor structure.
flags
is used to handle error checking and other aspects of conversion. It is the bitwise-or of several flag values including
SWIG_POINTER_EXCEPTION
SWIG_POINTER_DISOWN
. The first flag makes the function raise an exception on type error. The second flag additionally steals ownership of an object. Returns 0 on success and -1 on error.
将 Python 對象轉換為 C 指針。轉換結果放置在位于
obj
的指針中。
ptr
是 SWIG 類型的描述符結構體。
ty
用于處理錯誤檢查和轉換的其他方面。它是幾個标志值的按位或,包括
flags
SWIG_POINTER_EXCEPTION
。第一個标志使函數在類型錯誤時引發異常。第二個标志還竊取了對象的所有權。成功傳回 0,錯誤傳回 -1。
SWIG_POINTER_DISOWN
PyObject * SWIG_NewPointerObj(void *ptr, SWIG_type_info *ty, int own)
Creates a new Python pointer object.
ptr
is the pointer to convert,
ty
is the SWIG type descriptor structure that describes the type, and
own
is a flag that indicates whether or not Python should take ownership of the pointer.
Both of these functions require the use of a special SWIG type-descriptor structure. This structure contains information about the mangled name of the datatype, type-equivalence information, as well as information about converting pointer values under C++ inheritance. For a type of
Foo *
, the type descriptor structure is usually accessed as follows:
建立一個新的 Python 指針對象。是要轉換的指針,
ptr
是描述該類型的 SWIG 類型描述符結構體,而
ty
own
是訓示 Python 是否應擁有該指針所有權的标志。
這兩個功能都需要使用特殊的 SWIG 類型描述符結構體。此結構體包含有關資料類型的錯誤名稱,類型等效資訊以及有關在 C++ 繼承下轉換指針值的資訊。對于
類型,通常按以下方式通路類型描述符結構體:
Foo *
Foo *f;
if (! SWIG_IsOK( SWIG_ConvertPtr($input, (void **) &f, SWIG TYPE_p_Foo, 0))) {
SWIG_exception_fail( SWIG_TypeError, "in method '$symname', expecting type Foo");
}
PyObject *obj;
obj = SWIG_NewPointerObj(f, SWIG TYPE_p_Foo, 0);
In a typemap, the type descriptor should always be accessed using the special typemap variable
$1_descriptor
在類型映射中,應該始終使用特殊的類型映射變量 $1_descriptor
通路類型描述符。例如:
%typemap(in) Foo * {
if (! SWIG_IsOK( SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor, 0))) {
SWIG_exception_fail( SWIG_TypeError, "in method '$symname', expecting type Foo");
}
}
If necessary, the descriptor for any type can be obtained using the
$descriptor()
macro in a typemap. For example:
如有必要,可以使用類型映射中的 $descriptor()
宏擷取任何類型的描述符。例如:
%typemap(in) Foo * {
if (! SWIG_IsOK( SWIG_ConvertPtr($input, (void **) &$1, $descriptor(Foo *), 0))) {
SWIG_exception_fail( SWIG_TypeError, "in method '$symname', expecting type Foo");
}
}
Although the pointer handling functions are primarily intended for manipulating low-level pointers, both functions are fully aware of Python proxy classes. Specifically,
SWIG_ConvertPtr()
will retrieve a pointer from any object that has a
this
attribute. In addition,
SWIG_NewPointerObj()
can automatically generate a proxy class object (if applicable).
盡管指針處理功能主要用于操縱低級指針,但兩個功能都完全了解 Python 代理類。具體來說,将從具有
SWIG_ConvertPtr()
屬性的任何對象中檢索指針。另外,
this
可以自動生成代理類對象(如果适用)。
SWIG_NewPointerObj()
Using docstrings in Python code is becoming more and more important and more tools are coming on the scene that take advantage of them, everything from full-blown documentation generators to class browsers and popup call-tips in Python-aware IDEs. Given the way that SWIG generates the proxy code by default, your users will normally get something like
function_name(*args)
in the popup calltip of their IDE which is next to useless when the real function prototype might be something like this:
在 Python 代碼中使用 docstring 變得越來越重要,并且越來越多的工具可以利用它們來利用它們,從成熟的文檔生成器到類浏覽器以及 Python 感覺的 IDE 中的彈出式調用提示,無所不包。根據 SWIG 預設生成代理代碼的方式,你的使用者通常會在其 IDE 的彈出式調用提示中獲得類似 function_name(* args)
的内容,當實際函數原型可能像這樣時,該内容将無效:
bool function_name(int x, int y, Foo* foo=NULL, Bar* bar=NULL);
The features described in this section make it easy for you to add docstrings to your modules, functions and methods that can then be used by the various tools out there to make the programming experience of your users much simpler.
本節中描述的功能使你可以輕松地将文檔字元串添加到子產品,函數和方法中,然後可以被各種工具使用,以使使用者的程式設計體驗變得更加簡單。
36.10.1 子產品 docstring
docstring
Python allows a docstring at the beginning of the
.py
file before any other statements, and it is typically used to give a general description of the entire module. SWIG supports this by setting an option of the
%module
Python 允許在檔案開頭的文檔字元串在任何其他語句之前,并且通常用于提供整個子產品的一般描述。SWIG 通過設定
.py
指令的選項來支援這一點。例如:
%module
%module(docstring="This is the example module's docstring") example
When you have more than just a line or so then you can retain the easy readability of the
%module
directive by using a macro. For example:
如果你不隻是一行,那麼可以使用宏來保留 %module
指令的易讀性。例如:
%define DOCSTRING
"The `XmlResource` class allows program resources defining menus,
layout of controls on a panel, etc. to be loaded from an XML file."
%enddef
%module(docstring=DOCSTRING) xrc
36.10.2 %feature("autodoc")
%feature("autodoc")
As alluded to above SWIG will generate all the function and method proxy wrappers with just "args" (or "args, **kwargs" if the -keyword option is used) for a parameter list and will then sort out the individual parameters in the C wrapper code. This is nice and simple for the wrapper code, but makes it difficult to be programmer and tool friendly as anyone looking at the
.py
file will not be able to find out anything about the parameters that the functions accept.
But since SWIG does know everything about the function it is possible to generate a docstring containing the parameter types, names and default values. Since many of the docstring tools are adopting a standard of recognizing if the first thing in the docstring is a function prototype then using that instead of what they found from introspection, then life is good once more.
SWIG's Python module provides support for the "autodoc" feature, which when attached to a node in the parse tree will cause a docstring to be generated that includes the name of the function, parameter names, default values if any, and return type if any. There are also four levels for autodoc controlled by the value given to the feature,
%feature("autodoc", "*level*")
. The four values for level are covered in the following sub-sections.
如上所述,SWIG 将僅使用參數清單的(或
* args
* args
,如果使用
** kwargs
選項)來生成所有函數和方法代理包裝,然後對各個參數進行排序在 C 包裝器代碼中。對于包裝代碼來說,這是很好而且很簡單的方法,但是卻使程式員和工具友好起來變得很困難,因為任何檢視
-keyword
.py
檔案的人都無法找到有關函數接受的參數的任何資訊。
但是,由于 SWIG 确實了解有關該函數的所有資訊,是以可以生成包含參數類型,名稱和預設值的文檔字元串。由于許多文檔字元串工具都采用一種标準,即識别文檔字元串中的第一件事是否是函數原型,然後使用它而不是内省的結果,是以生活再好不過了。
SWIG 的 Python 子產品提供對“自動文檔”功能的支援,該功能附加到解析樹中的節點時将導緻生成文檔字元串,其中包括函數名稱,參數名稱,預設值(如果有)以及傳回類型(如果有) 。
也有四個級别,這些級别由功能值
autodoc
控制。以下各小節将介紹
%feature("autodoc", "*level*")
的四個值。
level
36.10.2.1 %feature("autodoc", "0")
%feature("autodoc", "0")
When level "0" is used then the types of the parameters will not be included in the autodoc string. For example, given this function prototype:
如果使用級别 ,則自動字元串中将不包括參數類型。例如,給定此函數原型:
%feature("autodoc", "0");
bool function_name(int x, int y, Foo* foo=NULL, Bar* bar=NULL);
Then Python code like this will be generated:
将産生這樣的 Python 代碼:
def function_name(*args, **kwargs):
"""function_name(x, y, foo=None, bar=None) -> bool"""
...
36.10.2.2 %feature("autodoc", "1")
%feature("autodoc", "1")
When level "1" is used then the parameter types will be used in the autodoc string. In addition, an attempt is made to simplify the type name such that it makes more sense to the Python user. Pointer, reference and const info is removed if the associated type is has an associated Python type (
%rename
's are thus shown correctly). This works most of the time, otherwise a C/C++ type will be used. See the next section for the "docstring" feature for tweaking the docstrings to your liking. Given the example above, then turning on the parameter types with level "1" will result in Python code like this:
,則将在自動文檔字元串中使用類型的參數。另外,嘗試簡化類型名稱,以使它對 Python 使用者更有意義。如果相關的類型具有相關的 Python 類型,則将删除指針、引用和 const 資訊(是以正确顯示了
1
)。這在大多數情況下都有效,否則将使用 C/C++ 類型。請參閱下一節的“文檔字元串”功能,以根據自己的喜好調整文檔字元串。給定上面的示例,然後打開級别為
%rename
的參數類型将得到如下 Python 代碼:
1
def function_name(*args, **kwargs):
"""function_name(int x, int y, Foo foo=None, Bar bar=None) -> bool"""
...
36.10.2.3 %feature("autodoc", "2")
%feature("autodoc", "2")
Level "2" results in the function prototype as per level "0". In addition, a line of documentation is generated for each parameter using numpydoc style. Using the previous example, the generated code will be:
級别 2
産生了根據級别 的功能原型。另外,還會使用 numpydoc 樣式為每個參數生成一行文檔。使用前面的示例,生成的代碼将是:
def function_name(*args, **kwargs):
"""
function_name(x, y, foo=None, bar=None) -> bool
Parameters
----------
x: int
y: int
foo: Foo *
bar: Bar *
"""
...
Note that the documentation for each parameter is sourced from the "doc" typemap which by default shows the C/C++ type rather than the simplified Python type name described earlier for level "1". Typemaps can of course change the output for any particular type, for example the
int x
parameter:
請注意,每個參數的文檔均來自類型映射,該映射預設情況下顯示 C/C++ 類型,而不是先前為級别
doc
所述的簡化的 Python 類型名稱。類型映射當然可以更改任何特定類型的輸出,例如
1
參數:
int x
%feature("autodoc", "2");
%typemap("doc") int x "$1_name (C++ type: $1_type) -- Input $1_name dimension"
bool function_name(int x, int y, Foo* foo=NULL, Bar* bar=NULL);
resulting in
def function_name(*args, **kwargs):
"""
function_name(x, y, foo=None, bar=None) -> bool
Parameters
----------
x (C++ type: int) -- Input x dimension
y: int
foo: Foo *
bar: Bar *
"""
36.10.2.4 %feature("autodoc", "3")
%feature("autodoc", "3")
Level "3" results in the function prototype as per level "1" but also contains the same additional line of documentation for each parameter as per level "2". Using our earlier example again, the generated code will be:
産生的函數原型為級别
3
,但還包含與級别
1
相同的每個參數的附加文檔行。再次使用前面的示例,生成的代碼将是:
2
def function_name(*args, **kwargs):
"""
function_name(int x, int y, Foo foo=None, Bar bar=None) -> bool
Parameters
----------
x: int
y: int
foo: Foo *
bar: Bar *
"""
...
36.10.2.5 %feature("autodoc", "docstring")
%feature("autodoc", "docstring")
Finally, there are times when the automatically generated autodoc string will make no sense for a Python programmer, particularly when a typemap is involved. So if you give an explicit value for the autodoc feature then that string will be used in place of the automatically generated string. For example:
最後,有時候自動生成的 autodoc 字元串對于 Python 程式員來說毫無意義,特别是當涉及到類型映射時。是以,如果為 autodoc 功能提供一個明确的值,則将使用該字元串代替自動生成的字元串。例如:
%feature("autodoc", "GetPosition() -> (x, y)") GetPosition;
void GetPosition(int* OUTPUT, int* OUTPUT);
36.10.3 %feature("docstring")
%feature("docstring")
In addition to the autodoc strings described above, you can also attach any arbitrary descriptive text to a node in the parse tree with the "docstring" feature. When the proxy module is generated then any docstring associated with classes, function or methods are output. If an item already has an autodoc string then it is combined with the docstring and they are output together. If the docstring is all on a single line then it is output like this:
除了上述自動文檔字元串之外,你還可以使用“文檔字元串”功能将任意描述性文本附加到解析樹中的節點上。生成代理子產品時,将輸出與類,函數或方法關聯的任何文檔字元串。如果項目已經具有自動文檔字元串,則将其與文檔字元串合并,然後将它們一起輸出。如果文檔字元串全部在一行上,則輸出如下:
"""This is the docstring"""
Otherwise, to aid readability it is output like this:
或者增加可讀性,則輸出:
"""
This is a multi-line docstring
with more than one line.
"""
Python has concepts of modules and packages. Modules are separate units of code and may be grouped together to form a package. Packages may be nested, that is they may contain subpackages. This leads to tree-like hierarchy, with packages as intermediate nodes and modules as leaf nodes.
The hierarchy of Python packages/modules follows the hierarchy of
*.py
files found in a source tree (or, more generally, in the Python path). Normally, the developer creates new module by placing a
*.py
file somewhere under Python path; the module is then named after that
*.py
file. A package is created by placing an
__init__.py
file within a directory; the package is then named after that directory. For example, the following source tree:
Python 具有子產品和包的概念。子產品是單獨的代碼單元,可以組合在一起形成一個包。程式包可能是嵌套的,即它們可能包含子程式包。這導緻樹狀層次結構,其中包作為中間節點,而子產品作為葉節點。
Python 包/子產品的層次結構遵循在源樹(或更常見的是在 Python 路徑中)中找到的
檔案的層次結構。通常,開發人員通過在 Python 路徑下的某個位置放置一個
*.py
檔案來建立新子產品;然後,該子產品以該
*.py
檔案命名。通過在目錄中放置一個
*.py
檔案來建立一個包。然後,以該目錄命名該軟體包。例如,以下源代碼樹:
__init__.py
mod1.py
pkg1/__init__.py
pkg1/mod2.py
pkg1/pkg2/__init__.py
pkg1/pkg2/mod3.py
defines the following Python packages and modules:
定義了以下 Python 包和子產品:
pkg1 # package
pkg1.pkg2 # package
mod1 # module
pkg1.mod2 # module
pkg1.pkg2.mod3 # module
The purpose of an
__init__.py
file is two-fold. First, the existence of
__init__.py
in a directory informs the Python interpreter that this directory contains a Python package. Second, the code in
__init__.py
is loaded/executed automatically when the package is initialized (when it or its submodule/subpackage gets
import
'ed). By default, SWIG generates proxy Python code – one
*.py
file for each
*.i
interface. The
__init__.py
files, however, are not generated by SWIG . They should be created by other means. Both files (module
*.py
__init__.py
) should be installed in appropriate destination directories in order to obtain a desirable package/module hierarchy.
Python3 adds another option for packages with PEP 0420 (implicit namespace packages). Implicit namespace packages no longer use
__init__.py
files. SWIG generated Python modules support implicit namespace packages. See 36.11.5 Implicit Namespace Packages for more information.
If you place a SWIG generated module into a Python package then there are details concerning the way SWIG searches for the wrapper module that you may want to familiarize yourself with.
The way Python defines its modules and packages impacts SWIG users. Some users may need to use special features such as the
package
option in the
%module
directive or import related command line options. These are explained in the following sections.
檔案的目的是雙重的。首先,目錄中存在
__init__.py
通知 Python 解釋器該目錄包含 Python 包。其次,初始化包時(當它或其子子產品/子包獲得導入時),将自動加載/執行
__init__.py
中的代碼。預設情況下,SWIG 生成代理 Python 代碼——每個
__init__.py
接口一個
*.i
檔案。但是,
* .py
檔案不是由 SWIG 生成的。它們應該通過其他方式建立。這兩個檔案(子產品
__init__.py
*.py
__init__.py
)都應安裝在适當的目标目錄中,以便獲得所需的包/子產品層次結構。
Python3 為 PEP 0420 的軟體包(隐式命名空間軟體包)添加了另一個選項。隐式命名空間包不再使用
__init__.py
檔案。SWIG 生成的 Python 子產品支援隐式命名空間包。有關更多資訊,請參見 36.11.5 隐式命名空間包。
如果将 SWIG 生成的子產品放入 Python 包中,那麼可能會有關于 SWIG 搜尋包裝器子產品的方式的詳細資訊想熟悉一下。
Python 定義其子產品和軟體包的方式會影響 SWIG 使用者。一些使用者可能需要使用特殊功能,例如
指令中的
%module
選項或導入相關的指令行選項。這些将在以下各節中說明。
package
Using the
package
%module
directive allows you to specify a Python package that the module will be in when installed.
%module
選項,可以指定安裝子產品時将在其中的 Python 軟體包。
package
%module(package="wx") xrc
This is useful when the
.i
file is
%import
ed by another
.i
file. By default SWIG will assume that the importer is able to find the importee with just the module name, but if they live in separate Python packages then this won't work. However if the importee specifies what its package is with the
%module
option then the Python code generated for the importer will use that package name when importing the other module and in base class declarations, etc..
SWIG assumes that the
package
option provided to
%module
together with the
module
name (that is,
wx.xrc
in the above example) forms a fully qualified (absolute) name of a module (in Python terms). This is important especially for Python 3, where absolute imports are used by default. It's up to you to place the generated module files (
.py
.so
) in appropriate subdirectories. For example, if you have an interface file
foo.i
with:
當檔案由另一個
.i
檔案進行導入時,這很有用。預設情況下,SWIG 将假定導入程式僅使用子產品名稱即可找到該導入對象,但是如果它們位于單獨的 Python 包中,則此方法将無效。但是,如果導入對象使用
.i
%module
選項指定了其軟體包,那麼為導入器生成的 Python 代碼将在導入其他子產品時以及在基類聲明等中使用該軟體包名稱。
SWIG 假定提供給
%module
選項和子產品名(在上面的示例中為
package
)一起構成了子產品的完全合格(絕對)名稱(使用 Python 術語) )。這對于 Python 3 特别重要,在 Python 3 中預設使用絕對導入。你可以将生成的子產品檔案(
wx.xrc
.py
)放在适當的子目錄中。例如,如果你有一個接口檔案
.so
,其中包含:
foo.i
%module(package="pkg1.pkg2") foo
then the resulting directory layout should be
然後結果目錄将是
pkg1/
pkg1/__init__.py
pkg1/pkg2/__init__.py
pkg1/pkg2/foo.py # (generated by SWIG)
pkg1/pkg2/_foo.so # (shared library built from C/C++ code generated by SWIG)
Suppose, we have the following hierarchy of files:
假設我們有如下架構的檔案:
pkg1/
pkg1/__init__.py
pkg1/mod2.py
pkg1/pkg2/__init__.py
pkg1/pkg2/mod3.py
Let the contents of
pkg1/pkg2/mod3.py
be
pkg1/pkg2/mod3.py
的内容是
class M3: pass
We edit
pkg1/mod2.py
and want to import module
pkg1/pkg2/mod3.py
in order to derive from class
M3
. We can write appropriate Python code in several ways, for example:
- Using
syntax with absolute package name:import <>
我們編輯,并想導入子產品
pkg1/mod2.py
,以便從類
pkg1/pkg2/mod3.py
派生。我們可以通過幾種方式編寫适當的 Python 代碼,例如:
M3
文法與包的絕對名字:
import <>
# pkg1/mod2.py
import pkg1.pkg2.mod3
class M2(pkg1.pkg2.mod3.M3):
pass
-
syntax with package name relative toimport <>
(only in Python 2.7 and earlier):pkg1
文法與相對于
import <>
的包名字(僅在 Python 2.7 及更早版本中):
pkg1
# pkg1/mod2.py
import pkg2.mod3
class M2(pkg2.mod3.M3):
pass
-
syntax (relative import syntax, only in Python 2.5 and later):from <> import <>
文法(相對導入文法,僅在 Python 2.5 及更早版本中):
from <> import <>
# pkg1/mod2.py
from .pkg2 import mod3
class M2(mod3.M3): pass
- Other variants, for example the following construction in order to have the
symbol available inpkg2.mod3.M3
as in point 2 above (but now under Python 3):mod2
- 其他變體,例如以下構造旨在使
中
mod2
是可用的,像之前的第 2 點(但現在在 Python 3 中):
pkg2.mod3.M3
# pkg1/mod2.py
from . import pkg2
from .pkg2 import mod3
class M2(pkg2.mod3.M3): pass
Now suppose we have
mod2.i
with
現在,假設我們有 mod2.i
// mod2.i
%module (package="pkg1") mod2
%import "mod3.i"
// ...
mod3.i
與 mod3.i
// mod3.i
%module (package="pkg1.pkg2") mod3
// ...
By default, SWIG would generate
mod2.py
proxy file with
import
directive as in point 1. This can be changed with the
-relativeimport
command line option. The
-relativeimport
instructs SWIG to organize imports as in point 2 (for Python < 2.7.0) or as in point 4 for Python 2.7.0 and newer. This is a check done at the time the module is imported. In short, if you have
mod2.i
mod3.i
as above, then without
-relativeimport
SWIG will write
預設情況下,SWIG 會使用第 1 點中的指令生成
import
代理檔案。這可以通過
mod2.py
指令行選項進行更改。
-relativeimport
訓示 SWIG 按照第 2 點(對于 Python < 2.7.0)或第 4 點(對于 Python 2.7.0 及更高版本)組織導入。這是在導入子產品時進行的檢查。簡而言之,如果你具有上述的
-relativeimport
mod2.i
,那麼在沒有
mod3.i
的情況下,SWIG 會将
-relativeimport
import pkg1.pkg2.mod3
to
mod2.py
proxy file, and with
-relativeimport
it will write
寫進代理檔案,如果有
mod2.py
它會寫
-relativeimport
from sys import version_info
if version_info >= (2, 7, 0):
from . import pkg2
import pkg1.pkg2.mod3
else:
import pkg2.mod3
del version_info
You should avoid using relative imports and use absolute ones whenever possible. There are some cases, however, when relative imports may be necessary. The first example is, when some (legacy) Python code refers entities imported by proxy files generated by SWIG , and it assumes that the proxy file uses relative imports. Second case is, when one puts import directives in
__init__.py
to import symbols from submodules or subpackages and the submodule depends on other submodules (discussed later).
你應避免使用相對導入,并盡可能使用絕對導入。但是,在某些情況下,可能需要相對導入。第一個示例是,當某些(舊式)Python 代碼引用由 SWIG 生成的代理檔案導入的實體時,它假定代理檔案使用相對導入。第二種情況是,當将導入指令放在 __init__.py
中以從子子產品或子包中導入符号,并且該子子產品依賴于其他子子產品時(稍後讨論)。
As you may know, there is an incompatibility in import semantics (for the
import <>
syntax) between Python 2 and 3. In Python 2.4 and earlier it is not clear whether
如你所知,Python 2 和 3 之間的導入語義(對于 import <>
文法)不相容。在 Python 2.4 和更低版本中,尚不清楚是否
import foo
refers to a top-level module or to another module inside the current package. In Python 3 it always refers to a top-level module (see PEP 328). To instruct Python 2.5 through 2.7 to use new semantics (that is
import foo
is interpreted as absolute import), one has to put the following line
指的是頂層子產品或目前包内的另一個子產品。在 Python 3 中,它始終是指頂級子產品(請參閱 PEP 328)。為了訓示 Python 2.5 到 2.7 使用新的語義(即, import foo
被解釋為絕對導入),必須放置以下行
from __future__ import absolute_import
at the very beginning of his proxy
*.py
file. In SWIG , it may be accomplished with
%pythonbegin
directive as follows:
在它的代理伺服器檔案的開頭。在 SWIG 中,可以通過
*.py
指令完成,如下所示:
%pythonbegin
%pythonbegin %{
from __future__ import absolute_import
%}
36.11.4 從 __init__.py
導入
__init__.py
Imports in
__init__.py
are handy when you want to populate a package's namespace with names imported from other modules. In SWIG based projects this approach may also be used to split large pieces of code into smaller modules, compile them in parallel and then re-assemble everything at runtime by importing submodules' contents in
__init__.py
, for example.
Unfortunately import directives in
__init__.py
may cause problems, especially if they refer to a package's submodules. This is caused by the way Python initializes packages. If you spot problems with imports from
__init__.py
try using
-relativeimport
option. Below we explain in detail one issue, for which the
-relativeimport
workaround may be helpful.
Consider the following example (Python 3):
當你想使用從其他子產品導入的名稱填充包的命名空間時,在中的導入很友善。在基于 SWIG 的項目中,這種方法也可以用于将大段代碼拆分為較小的子產品,并行編譯它們,然後在運作時通過将子子產品的内容導入到
__init__.py
__init__.py
中來重新組裝所有内容。
不幸的是,
中的
__init__.py
指令可能會引起問題,尤其是當它們引用包的子子產品時。這是由 Python 初始化程式包的方式引起的。如果你發現從
import
導入的問題,請嘗試使用
__init__.py
選項。下面我們詳細解釋一個問題,
-relativeimport
-relativeimport
解決方法可能會對你有所幫助。
考慮以下示例(Python 3):
pkg1/__init__.py # (empty)
pkg1/pkg2/__init__.py # (imports something from bar.py)
pkg1/pkg2/foo.py
pkg1/pkg2/bar.py # (imports foo.py)
If the file contents are:
如果檔案内容是:
-
pkg1/pkg2/__init__.py:
# pkg1/pkg2/__init__.py
from .bar import Bar
-
pkg1/pkg2/foo.py:
# pkg1/pkg2/foo.py
class Foo: pass
-
pkg1/pkg2/bar.py:
# pkg1/pkg2/bar.py
import pkg1.pkg2.foo
class Bar(pkg1.pkg2.foo.Foo): pass
Now if one simply used
import pkg1.pkg2
, it will usually fail:
現在,如果有人簡單使用 import pkg1.pkg2
,通常會失敗:
>>> import pkg1.pkg2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "./pkg1/pkg2/__init__.py", line 2, in <module>
from .bar import Bar
File "./pkg1/pkg2/bar.py", line 3, in <module>
class Bar(pkg1.pkg2.foo.Foo): pass
AttributeError: 'module' object has no attribute 'pkg2'
Surprisingly, if we execute the
import pkg1.pkg2
directive for the second time, it succeeds. The reason seems to be following: when Python spots the
from .bar import Bar
directive in
pkg1/pkg2/__init__.py
it starts loading
pkg1/pkg2/bar.py
. This module imports
pkg1.pkg2.foo
in turn and tries to use
pkg1.pkg2.foo.Foo
, but the package
pkg1
is not fully initialized yet (the initialization procedure is actually in progress) and it seems like the effect of the already seen
import pkg1.pkg2.pkg3.foo
is "delayed" or ignored. Exactly the same may happen to a proxy module generated by SWIG .
One workaround for this case is to use a relative import in
pkg1/pkg2/bar.py
. If we change
bar.py
to be:
令人驚訝的是,如果我們第二次執行指令,則該指令成功。原因似乎如下:當 Python 在
import pkg1.pkg2
中發現
pkg1/pkg2/__ init__.py
指令時,它開始加載
from .bar import Bar
。這個子產品依次導入
pkg1/pkg2/bar.py
并嘗試使用
pkg1.pkg2.foo
,但是包
pkg1.pkg2.foo.Foo
尚未完全初始化(初始化過程實際上正在進行中),看起來像已經看到的
pkg1
import pkg1.pkg2.pkg3.foo
的效果被延遲或忽略。SWIG 生成的代理子產品可能完全相同。
這種情況的一種解決方法是在
中使用相對導入。如果我們将
pkg1/pkg2/bar.py
更改為:
bar.py
from .pkg3 import foo
class Bar(foo.Foo): pass
from . import pkg3
from .pkg3 import foo
class Bar(pkg3.foo.Foo): pass
then the example works again. With SWIG , you need to enable the
-relativeimport
option in order to have the above workaround in effect (note, that the Python 2 case also needs the
-relativeimport
workaround).
然後該示例再次起作用。使用 SWIG,你需要啟用選項才能生效上述解決方法(請注意,Python 2 案例也需要
-relativeimport
解決方法)。
-relativeimport
Python 3.3 introduced PEP 0420 which implements implicit namespace packages. In a nutshell, implicit namespace packages remove the requirement of an init.py file and allow packages to be split across multiple PATH elements. For example:
Python 3.3 引入了 PEP 0420,它實作了隐式命名空間包。簡而言之,隐式命名空間包消除了 __init__.py
檔案的要求,并允許将包拆分為多個 PATH 元素。例如:
/fragment1/pkg1/mod1.py
/fragment2/pkg1/mod2.py
/fragment3/pkg1/mod3.py
If PYTHONPATH is set to "/fragment1:/fragment2:/fragment3", then mod1, mod2 and mod3 will be part of pkg1. This allows for splitting of packages into separate pieces. This can be useful for SWIG generated wrappers in the following way.
Suppose you create a SWIG wrapper for a module called robin. The SWIG generated code consists of two files robin.py and _robin.so. You wish to make these modules part of a subpackage (brave.sir). With implicit namespace packages you can place these files in the following configurations:
Using PYTHONPATH="/some/path"
如果将 PYTHONPATH 設定為,則
/fragment1:/fragment2:/fragment3
mod1
mod2
将成為
mod3
pkg1
的一部分。這允許将包裝分成獨立的部分。通過以下方式,這對于 SWIG 生成的包裝器很有用。
假設你為名為
的子產品建立了 SWIG 包裝器。SWIG 生成的代碼包含兩個檔案
robin
robin.py
。你希望使這些子產品成為子包(
_robin.so
)的一部分。使用隐式命名空間包,可以将這些檔案置于以下配置中:
brave.sir
PYTHONPATH="/some/path"
/some/path/brave/sir/robin.py
/some/path/brave/sir/_robin.so
Using PYTHONPATH="/some/path:/some/other/path"
PYTHONPATH="/some/path:/some/other/path"
/some/path/brave/sir/robin.py
/some/other/path/brave/sir/_robin.so
Finally suppose that your pure python code is stored in a .zip file or some other way (database, web service connection, etc). Python can load the robin.py module using a custom importer. But the _robin.so module will need to be located on a file system. Implicit namespace packages make this possible. For example, using PYTHONPATH="/some/path/foo.zip:/some/other/path"
Contents of foo.zip
最後,假設你的純 Python 代碼存儲在檔案中或其他某種方式(資料庫,Web服務連接配接等)中。Python 可以使用自定義導入器加載
.zip
子產品。但是
robin.py
子產品将需要位于檔案系統上。隐式命名空間包使這成為可能。例如,使用
_robin.so
PYTHONPATH="/some/path/foo.zip:/some/other/path"
的内容
foo.zip
brave/
brave/sir/
brave/sir/robin.py
File system contents
檔案系統内容
/some/other/path/brave/sir/_robin.so
Support for implicit namespace packages was added to python-3.3. The zipimporter requires python-3.5.1 or newer to work with subpackages.
Compatibility Note: Support for implicit namespace packages was added in SWIG -3.0.9.
對隐式命名空間包的支援已添加到 python-3.3 中。
zipimporter
需要 python-3.5.1 或更高版本才能使用子包。
相容性說明:在 SWIG -3.0.9 中添加了對隐式命名空間包的支援。
When SWIG creates wrappers from an interface file, say foo.i, two Python modules are created. There is a pure Python module module (foo.py) and C/C++ code which is built and linked into a dynamically (or statically) loaded low-level module _foo (see the Preliminaries section for details). So, the interface file really defines two Python modules. How these two modules are loaded is covered next.
The pure Python module needs to load the C/C++ module in order to link to the wrapped C/C++ methods. To do this it must make some assumptions about what package the C/C++ module may be located in. The approach the pure Python module uses to find the C/C++ module is as follows:
- The pure Python module, foo.py, tries to load the C/C++ module, _foo, from the same package foo.py is located in. The package name is determined from the
attribute given to foo.py by the Python loader that imported foo.py. If foo.py is not in a package then _foo is loaded as a global module.__name__
- If the above import of_foo results in an ImportError being thrown, then foo.py makes a final attempt to load _foo as a global module.
The Python code implementing the loading logic described above is quite complex to handle multiple versions of Python, but it can be replaced with custom code. This is not recommended unless you understand the full intricacies of importing Python modules. The custom code can be specified by setting the
moduleimport
option of the
%module
directive with the appropriate import code. For example:
當 SWIG 從接口檔案(例如)建立包裝器時,将建立兩個 Python 子產品。有一個純 Python 子產品子產品(
foo.i
)和 C/C++ 代碼,它們被建構并連結到動态(或靜态)加載的低級子產品
foo.py
_foo
中(請參閱預備知識部分)。是以,接口檔案确實定義了兩個 Python 子產品。接下來介紹如何加載這兩個子產品。
純 Python 子產品需要加載 C/C++ 子產品才能連結到包裝的 C/C++ 方法。為此,必須對 C/C++ 子產品可能位于的包進行一些假設。純 Python 子產品用來查找 C/C++ 子產品的方法如下:
1.純 Python 子產品
嘗試從
foo.py
所在的同一程式包中加載 C/C++ 子產品
foo.py
。程式包名稱由
_foo
提供的
foo.py
屬性确定導入
__name__
的 Python 加載器。如果
foo.py
不在軟體包中,則
foo.py
_foo
作為全局子產品加載。
2.如果以上導入的
導緻引發
_foo
ImportError
最終嘗試将
foo.py
_foo
加載為全局子產品。
實作上述加載邏輯的 Python 代碼非常複雜,無法處理多個版本的 Python,但可以用自定義代碼替換。除非你了解導入 Python 子產品的全部複雜性,否則不建議這樣做。可以通過使用适當的導入代碼設定
指令的
%module
選項來指定自定義代碼。例如:
moduleimport
%module(moduleimport="import _foo") foo
The special variable
$module
will also be expanded into the low-level C/C++ module name,
_foo
in the case above. When you have more than just a line or so then you can retain the easy readability of the
%module
在上述情況下,特殊變量也将擴充為低級 C/C++ 子產品名稱
$module
。如果你不隻是一行,那麼可以使用宏來保留
_foo
%module
%define MODULEIMPORT
"
print 'Loading low-level module $module'
import $module
print 'Module has loaded'
"
%enddef
%module(moduleimport=MODULEIMPORT) foo
Now let's consider an example using the SWIG default loading logic. Suppose foo.i is compiled into foo.py and _foo.so. Assuming /dir is on PYTHONPATH, then the two modules can be installed and used in the following ways:
現在,讓我們考慮一個使用 SWIG 預設加載邏輯的示例。假設被編譯為
foo.i
foo.py
。假設
_foo.so
在 PYTHONPATH 上,則可以通過以下方式安裝和使用這兩個子產品:
/dir
Both modules are in one package:
兩個子產品在同一個包中:
/dir/package/foo.py
/dir/package/__init__.py
/dir/package/_foo.so
And imported with
如下載下傳入
from package import foo
The pure python module is in a package and the C/C++ module is global:
純 Python 子產品在一個包中并且 C/C++ 子產品是全局的:
/dir/package/foo.py
/dir/package/__init__.py
/dir/_foo.so
from package import foo
Both modules are global:
兩個子產品皆為全局:
/dir/foo.py
/dir/_foo.so
import foo
If_foo is statically linked into an embedded Python interpreter, then it may or may not be in a Python package. This depends in the exact way the module was loaded statically. The above search order will still be used for statically loaded modules. So, one may place the module either globally or in a package as desired.
如果 _foo
被靜态連結到嵌入式 Python 解釋器中,則它可能在 Python 包中,也可能不在。這取決于子產品靜态加載的确切方式。上面的搜尋順序仍将用于靜态加載的子產品。是以,可以根據需要将子產品全局放置或打包放置。
It is strongly recommended to use dynamically linked modules for the C portion of your pair of Python modules. If for some reason you still need to link the C module of the pair of Python modules generated by SWIG into your interpreter, then this section provides some details on how this impacts the pure Python modules ability to locate the other part of the pair. Please also see the Static Linking section.
When Python is extended with C code the Python interpreter needs to be informed about details of the new C functions that have been linked into the executable. The code to do this is created by SWIG and is automatically called in the correct way when the module is dynamically loaded. However when the code is not dynamically loaded (because it is statically linked) Then the initialization method for the module created by SWIG is not called automatically and the Python interpreter has no idea that the new SWIG C module exists.
Before Python 3, one could simply call the init method created by SWIG which would have normally been called when the shared object was dynamically loaded. The specific name of this method is not given here because statically linked modules are not encouraged with SWIG (Static Linking). However one can find this init function in the C file generated by SWIG .
If you are really keen on static linking there are two ways to initialize the SWIG generated C module with the init method. Which way you use depends on what version of Python your module is being linked with. Python 2 and Python 3 treat this init function differently. And the way they treat it affects how the pure Python module will be able to locate the C module.
The details concerning this are covered completly in the documentation for Python itself. Links to the relavent sections follow:
- Extending in python2
- Extending in python3
There are two keys things to understand. The first is that in Python 2 the init() function returns void. In Python 3 the init() function returns a PyObject * which points to the new module. Secondly, when you call the init() method manually, you are the Python importer. So, you determine which package the C module will be located in.
So, if you are using Python 3 it is important that you follow what is described in the Python documentation linked above. In particular, you can't simply call the init() function generated by SWIG and cast the PyObject pointer it returns over the side. If you do then Python 3 will have no idea that your C module exists and the pure Python half of your wrapper will not be able to find it. You need to register your module with the Python interpreter as described in the Python docs.
With Python 2 things are somewhat more simple. In this case the init function returns void. Calling it will register your new C module as a global module. The pure Python part of the SWIG wrapper will be able to find it because it tries both the pure Python module it is part of and the global module. If you wish not to have the statically linked module be a global module then you will either need to refer to the Python documentation on how to do this (remember you are now the Python importer) or use dynamic linking.
強烈建議對 Python 子產品對應的 C 部分使用動态連結的子產品。如果由于某種原因你仍然需要将 SWIG 生成的一對 Python 子產品中的 C 子產品連結到你的解釋器中,那麼本節将提供一些詳細資訊如何影響純 Python 子產品的能力來定位配對中的另一部分。另請參見靜态連結部分。
使用 C 代碼擴充 Python 後,需要通知 Python 解釋器有關已連結到可執行檔案中的 C 函數的詳細資訊。執行此操作的代碼由 SWIG 建立,并在動态加載子產品時以正确的方式自動調用該代碼。但是,如果代碼不是動态加載的(因為它是靜态連結的),則不會自動調用 SWIG 建立的子產品的初始化方法,Python 解釋器也不知道新的 SWIG C 子產品存在。
在 Python 3 之前,可以簡單地調用 SWIG 建立的
init
方法,該方法通常在動态加載共享對象時被調用。此處未提供此方法的具體名稱,因為 SWIG 不鼓勵使用靜态連結的子產品(靜态連結)。但是,可以在 SWIG 生成的 C 檔案中找到此初始化函數。
如果你真的熱衷于靜态連結,則有兩種方法可以使用
方法初始化 SWIG 生成的 C 子產品。使用哪種方式取決于要與子產品連結的 Python 版本。Python 2 和 Python 3 對此
init
init
函數的處理方式有所不同。它們對待它的方式會影響純 Python 子產品将如何定位 C 子產品。
有關此方面的詳細資訊,在 Python 本身的文檔中已全面介紹。指向相關部分的連結如下:
有兩個關鍵的事情要了解。首先是在 Python 2 中,
函數傳回
init()
。在 Python 3 中,
void
函數傳回一個
init()
,它指向新子產品。其次,當你手動調用
PyObject *
init()
方法時,你就是 Python 導入器。是以,你可以确定 C 子產品将位于哪個軟體包中。
是以,如果你使用的是 Python 3,請務必遵循上面連結的 Python 文檔中所述的内容。特别是,你不能簡單地調用 SWIG 生成的
函數并将其傳回的
init()
PyObject
指針強制轉換為側面。如果這樣做,Python 3 将不知道 C 子產品的存在,并且包裝的純 Python 部分将無法找到它。你需要按照 Python 文檔中的說明向 Python 解釋器注冊子產品。
使用 Python 2,事情會更簡單。在這種情況下,
函數将傳回
init
。調用它會将你的新 C 子產品注冊為 global 子產品。SWIG 包裝程式的純 Python 部分将能夠找到它,因為它會嘗試同時包含它的純 Python 子產品和全局子產品。如果你不希望将靜态連結的子產品作為全局子產品,則需要參考 Python 文檔以了解如何執行此操作(請記住你現在是 Python 導入者)或使用動态連結。
void
SWIG is able to support Python 3.0. The wrapper code generated by SWIG can be compiled with both Python 2.x or 3.0. Further more, by passing the
-py3
command line option to SWIG , wrapper code with some Python 3 specific features can be generated (see below subsections for details of these features). The
-py3
option also disables some incompatible features for Python 3, such as
-classic
There is a list of known-to-be-broken features in Python 3:
- No more support for FILE* typemaps, because PyFile_AsFile has been dropped in Python 3.
-
command line option is removed and generating code using apply() is no longer supported.-apply
The following are Python 3.0 new features that are currently supported by SWIG .
SWIG 能夠支援 Python 3.0。SWIG 生成的包裝器代碼可以使用 Python 2.x 或 3.0 進行編譯。此外,通過将指令行選項傳遞給 SWIG,可以生成具有某些 Python 3 特定功能的包裝器代碼(有關這些功能的詳細資訊,請參見以下小節)。
-py3
選項還禁用了 Python 3 的某些不相容功能,例如
-py3
Python 3 中列出了一系列已知的功能:
-classic
以下是 SWIG 目前支援的 Python 3.0 新功能。
- 不再支援
類型映射,因為
FILE *
已在 Python 3 中删除。
PyFile_AsFile
- 删除了
指令行選項,并且不再支援使用
-apply
生成代碼。
apply()
-py3
option will enable function annotation support. When used SWIG is able to generate proxy method definitions like this:
-py3
選項将啟用功能注釋支援。使用 SWIG 時,可以生成如下的代理方法定義:
def foo(self, bar : "int"=0) -> "void" : ...
Also, even if without passing SWIG the
-py3
option, the parameter list still could be generated:
同樣,即使沒有通過 SWIG 的 -py3
選項,仍然可以生成參數清單:
def foo(self, bar=0): ...
But for overloaded function or method, the parameter list would fallback to
*args
self, *args
**kwargs
may be append depend on whether you enabled the keyword argument. This fallback is due to all overloaded functions share the same function in SWIG generated proxy class.
For detailed usage of function annotation, see PEP 3107.
但是對于重載的函數或方法,參數清單将回退到
*args
self, *args
**kwargs
,取決于你是否啟用了關鍵字參數。此後退是由于所有重載函數在 SWIG 生成的代理類中共享相同的函數。
函數标注的詳細資訊參見 PEP 3107。
Buffer protocols were revised in Python 3. SWIG also gains a series of new typemaps to support buffer interfaces. These typemap macros are defined in
pybuffer.i
, which must be included in order to use them. By using these typemaps, your wrapped function will be able to accept any Python object that exposes a suitable buffer interface.
For example, the
get_path()
function puts the path string into the memory pointed to by its argument:
緩沖區協定已在 Python 3 中進行了修訂。SWIG 還獲得了一系列新的類型映射,以支援緩沖區接口。這些類型映射宏在
pybuffer.i
中定義,必須使用它們才能使用它們。通過使用這些類型映射,你的包裝函數将能夠接受任何公開适當緩沖區接口的 Python 對象。
例如,
函數将路徑字元串放入其參數所指向的記憶體中:
get_path()
void get_path(char *s);
Then you can write a typemap like this: (the following example is applied to both Python 3.0 and 2.6, since the
bytearray
type is backported to 2.6).
然後,你可以編寫這樣的類型映射:(以下示例同時适用于 Python 3.0 和 2.6,因為 bytearray
類型已反向移植到 2.6)。
%include <pybuffer.i>
%pybuffer_mutable_string(char *str);
void get_path(char *s);
And then on the Python side the wrapped
get_path
could be used in this way:
然後在 Python 端,可以通過這種方式使用包裝的 get_path
>>> p = bytearray(10)
>>> get_path(p)
>>> print(p)
bytearray(b'/Foo/Bar/\x00')
The macros defined in
pybuffer.i
are similar to those in
cstring.i
中的宏定義類似于
pybuffer.i
cstring.i
%pybuffer_mutable_binary(parm, size_parm)
The macro can be used to generate a typemap which maps a buffer of an object to a pointer provided by
parm
and a size argument provided by
size_parm
宏可用于生成類型映射,該映射将對象的緩沖區映射到由提供的指針和由
parm
size_parm
參數。例如:
size
%pybuffer_mutable_binary(char *str, size_t size);
...
int snprintf(char *str, size_t size, const char *format, ...);
>>> buf = bytearray(6)
>>> snprintf(buf, "Hello world!")
>>> print(buf)
bytearray(b'Hello\x00')
>>>
%pybuffer_mutable_string(parm)
This typemap macro requires the buffer to be a zero terminated string, and maps the pointer of the buffer to
parm
這個類型映射要求緩沖區必須是一個零終止的字元串,并将緩沖區的指針映射到 parm
%pybuffer_mutable_string(char *str);
...
size_t make_upper(char *str);
>>> buf = bytearray(b'foo\x00')
>>> make_upper(buf)
>>> print(buf)
bytearray(b'FOO\x00')
>>>
Both
%pybuffer_mutable_binary
%pybuffer_mutable_string
require the provided buffer to be mutable, eg. they can accept a
bytearray
type but can't accept an immutable
byte
type.
%pybuffer_mutable_binary
都要求提供的緩沖區是可變的,例如。它們可以接受
%pybuffer_mutable_string
類型,但不能接受不可變的
bytearray
類型。
byte
%pybuffer_binary(parm, size_parm)
This macro maps an object's buffer to a pointer
parm
and a size
size_parm
. It is similar to
%pybuffer_mutable_binary
, except the
%pybuffer_binary
an accept both mutable and immutable buffers. As a result, the wrapped function should not modify the buffer.
該宏将對象的緩沖區映射到指針和大小
parm
。它與
size_parm
類似,除了
%pybuffer_mutable_binary
接受可變和不可變的緩沖區。是以,包裝函數不應修改緩沖區。
%pybuffer_binary
%pybuffer_string(parm)
This macro maps an object's buffer as a string pointer
parm
%pybuffer_mutable_string
but the buffer could be both mutable and immutable. And your function should not modify the buffer.
該宏将對象的緩沖區映射為字元串指針
parm
類似,但是緩沖區可能是既是可變的又是不可變的。而且你的函數不應修改緩沖區。
%pybuffer_mutable_string
By including
pyabc.i
and using the
-py3
command line option when calling SWIG , the proxy classes of the STL containers will automatically gain an appropriate abstract base class. For example, the following SWIG interface:
通過在調用 SWIG 時包含并使用
pyabc.i
指令行選項,STL 容器的代理類将自動獲得适當的抽象基類。例如,以下 SWIG 接口:
-py3
%include <pyabc.i>
%include <std_map.i>
%include <std_list.i>
namespace std {
%template(Mapii) map<int, int>;
%template(IntList) list<int>;
}
will generate a Python proxy class
Mapii
inheriting from
collections.MutableMap
and a proxy class
IntList
collections.MutableSequence
pyabc.i
also provides a macro
%pythonabc
that could be used to define an abstract base class for your own C++ class:
将産生一個繼承自的 Python 代理類
collections.MutableMap
和一個繼承自
Mapii
的代理類
collections.MutableSequence
IntList
還提供了宏
pyabc.i
,可用于為你自己的 C++ 類定義抽象基類:
%pythonabc
%pythonabc(MySet, collections.MutableSet);
For details of abstract base class, please see PEP 3119.
抽象基類的詳細資訊參見 PEP 3119。
By default, any byte string (
char*
std::string
) returned from C or C++ code is decoded to text as UTF-8. This decoding uses the
surrogateescape
error handler under Python 3.1 or higher -- this error handler decodes invalid byte sequences to high surrogate characters in the range U+DC80 to U+DCFF. As an example, consider the following SWIG interface, which exposes a byte string that cannot be completely decoded as UTF-8:
預設情況下,将從 C 或 C++ 代碼傳回的任何位元組字元串(
char *
)解碼為 UTF-8 文本。此解碼使用 Python 3.1 或更高版本下的
std::string
錯誤處理程式——該錯誤處理程式将無效位元組序列解碼為 U+DC80 到 U+DCFF 範圍内的高替代字元。例如,請考慮以下 SWIG 接口,該接口公開了無法完全解碼為 UTF-8 的位元組字元串:
surrogateescape
%module example
%include <std_string.i>
%inline %{
const char* non_utf8_c_str(void) {
return "h\xe9llo w\xc3\xb6rld";
}
%}
When this method is called from Python 3, the return value is the following text string:
從 Python 3 調用此方法時,傳回值為以下文本字元串:
>>> s = example.non_utf8_c_str()
>>> s
'h\udce9llo wörld'
Since the C string contains bytes that cannot be decoded as UTF-8, those raw bytes are represented as high surrogate characters that can be used to obtain the original byte sequence:
由于 C 字元串包含無法解碼為 UTF-8 的位元組,是以這些原始位元組被表示為高替代字元,可用于擷取原始位元組序列:
>>> b = s.encode('utf-8', errors='surrogateescape')
>>> b
b'h\xe9llo w\xc3\xb6rld'
One can then attempt a different encoding, if desired (or simply leave the byte string as a raw sequence of bytes for use in binary protocols):
然後,如果需要,可以嘗試進行不同的編碼(或簡單地将位元組字元串保留為原始位元組序列以供二進制協定使用):
>>> b.decode('latin-1')
'héllo wörld'
Note, however, that text strings containing surrogate characters are rejected with the default
strict
codec error handler. For example:
但是請注意,包含代理字元的文本字元串将被預設的 strict
編解碼器錯誤處理程式拒絕。例如:
>>> with open('test', 'w') as f:
... print(s, file=f)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
UnicodeEncodeError: 'utf-8' codec can't encode character '\udce9' in position 1: surrogates not allowed
This requires the user to check most strings returned by SWIG bindings, but the alternative is for a non-UTF8 byte string to be completely inaccessible in Python 3 code.
For more details about the
surrogateescape
error handler, please see PEP 383.
In some cases, users may wish to instead handle all byte strings as bytes objects in Python 3. This can be accomplished by adding
SWIG_PYTHON_STRICT_BYTE_CHAR
to the generated code:
這要求使用者檢查 SWIG 綁定傳回的大多數字元串,但是另一種方法是在 Python 3 代碼中完全不可通路非 UTF8 位元組的字元串。
有關
surrogateescape
錯誤處理程式的更多詳細資訊,請參見 PEP 383。
在某些情況下,使用者可能希望将所有位元組字元串作為 Python 3 中的位元組對象來處理。這可以通過在生成的代碼中添加
來實作:
SWIG_PYTHON_STRICT_BYTE_CHAR
%module char_to_bytes
%begin %{
#define SWIG_PYTHON_STRICT_BYTE_CHAR
%}
char *charstring(char *s) {
return s;
}
This will modify the behavior so that only Python 3 bytes objects will be accepted and converted to a C/C++ string, and any string returned from C/C++ will be converted to a bytes object in Python 3:
這将修改行為,以便僅 Python 3 位元組對象将被接受并轉換為 C/C++ 字元串,并且從 C/C++ 傳回的任何字元串都将在 Python 3 中轉換為位元組對象:
>>> from char_to_bytes import *
>>> charstring(b"hi") # Byte string
b'hi'
>>> charstring("hi") # Unicode string
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: in method 'charstring', argument 1 of type 'char *'
Note that in Python 2, defining
SWIG_PYTHON_STRICT_BYTE_CHAR
has no effect, since strings in Python 2 are equivalent to Python 3 bytes objects. However, there is a similar capability to force unicode-only handling for wide characters C/C++ strings (
wchar_t *
std::wstring
types) in Python 2. By default, in Python 2 both strings and unicode strings are converted to C/C++ wide strings, and returned wide strings are converted to a Python unicode string. To instead only convert unicode strings to wide strings, users can add
SWIG_PYTHON_STRICT_UNICODE_WCHAR
請注意,在 Python 2 中,定義無效,因為 Python 2 中的字元串等效于 Python 3 位元組對象。但是,在 Python 2 中,有類似的功能可以對寬字元 C/C++ 字元串(
SWIG_PYTHON_STRICT_BYTE_CHAR
wchar_t *
類型)強制僅對 unicode 進行處理。預設情況下,在 Python 2 中,字元串和 unicode 字元串都是轉換為 C/C++ 寬字元串,然後将傳回的寬字元串轉換為 Python unicode 字元串。要隻将 unicode 字元串轉換為寬字元串,使用者可以将
std::wstring
添加到生成的代碼中:
SWIG_PYTHON_STRICT_UNICODE_WCHAR
%module wchar_to_unicode
%begin %{
#define SWIG_PYTHON_STRICT_UNICODE_WCHAR
%}
wchar_t *wcharstring(wchar_t *s) {
return s;
}
This ensures that only unicode strings are accepted by wcharstring in both Python 2 and Python 3:
這確定隻有 unicode 字元串被 Python 2 和 Python 3 中的寬字元串接受:
>>> from wchar_to_unicode import *
>>> wcharstring(u"hi") # Unicode string
u'hi'
>>> wcharstring(b"hi") # Byte string
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: in method 'charstring', argument 1 of type 'wchar_t *'
By defining both
SWIG_PYTHON_STRICT_BYTE_CHAR
SWIG_PYTHON_STRICT_UNICODE_WCHAR
, Python wrapper code can support overloads taking both
std::string
(as Python bytes) and
std::wstring
(as Python unicode).
通過定義
SWIG_PYTHON_STRICT_BYTE_CHAR
,Python 包裝代碼可以支援接受
SWIG_PYTHON_STRICT_UNICODE_WCHAR
(Python 位元組)和
std::string
(Python unicode)的重載。
std::wstring
A Python 3 string is a Unicode string so by default a Python 3 string that contains Unicode characters passed to C/C++ will be accepted and converted to a C/C++ string (
char *
std::string
types). A Python 2 string is not a unicode string by default and should a Unicode string be passed to C/C++ it will fail to convert to a C/C++ string (
char *
std::string
types). The Python 2 behavior can be made more like Python 3 by defining
SWIG_PYTHON_2_UNICODE
when compiling the generated C/C++ code. By default when the following is wrapped:
Python 3 字元串是 Unicode 字元串,是以預設情況下,包含傳遞給 C/C++ 的 Unicode 字元的 Python 3 字元串将被接受并轉換為 C/C++ 字元串(
char *
類型)。預設情況下,Python 2 字元串不是 Unicode 字元串,并且如果将 Unicode 字元串傳遞給 C/C++,它将無法轉換為 C/C++ 字元串(
std::string
char *
類型)。通過在編譯生成的 C/C++ 代碼時定義
std::string
,可以使 Python 2 的行為更像 Python 3。預設情況下,包裝以下内容時:
SWIG_PYTHON_2_UNICODE
%module unicode_strings
char *charstring(char *s) {
return s;
}
An error will occur when using Unicode strings in Python 2:
當在 Python 2 中使用 Unicode 字元串,會出現一個錯誤:
>>> from unicode_strings import *
>>> charstring("hi")
'hi'
>>> charstring(u"hi")
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: in method 'charstring', argument 1 of type 'char *'
When the
SWIG_PYTHON_2_UNICODE
macro is added to the generated code:
當宏 SWIG_PYTHON_2_UNICODE
被加入生成代碼:
%module unicode_strings
%begin %{
#define SWIG_PYTHON_2_UNICODE
%}
char *charstring(char *s) {
return s;
}
Unicode strings will be successfully accepted and converted from UTF-8, but note that they are returned as a normal Python 2 string:
Unicode 字元串将被成功接受并從 UTF-8 轉換,但是請注意,它們将作為普通的 Python 2 字元串傳回:
>>> from unicode_strings import *
>>> charstring("hi")
'hi'
>>> charstring(u"hi")
'hi'
>>>
Note that defining both
SWIG_PYTHON_2_UNICODE
SWIG_PYTHON_STRICT_BYTE_CHAR
at the same time is not allowed, since the first is allowing unicode conversion and the second is explicitly prohibiting it.
請注意,不允許同時定義
SWIG_PYTHON_2_UNICODE
,因為前者允許 Unicode 轉換,而後者則明确禁止 Unicode 轉換。
SWIG_PYTHON_STRICT_BYTE_CHAR
★ 持續學習 ★ 堅持創作 ★