使用Boost.Python構建混合系統(譯)

Building Hybrid Systems with Boost.Python

使用Boost.Python構建混合系統前端

Author: David Abrahams
Contact: dave@boost-consulting.com
Organization: Boost Consulting
Date: 2003-05-14
Author: Ralf W. Grosse-Kunstleve
Copyright: Copyright David Abrahams and Ralf W. Grosse-Kunstleve 2003. All rights reservedpython

目錄程序員

摘要(Abstract)

Boost.Python是一個開源C++庫,它提供了一個相似IDL的簡潔接口,用於將C++類和函數綁定到Python。利用C++編譯自省功能和元編程技術,Boost.Python徹底用純C++實現的,而沒有引入新的語法。Boost.Python豐富的特性集和高級接口,使得徹底按混合系統設計軟件包成爲可能,並讓程序員以輕鬆連貫的方式,同時使用C++高效的編譯時多態,和Python極端便利的運行時多態。算法

Boost.Python is an open source C++ library which provides a concise IDL-like interface for binding C++ classes and functions to Python. Leveraging the full power of C++ compile-time introspection and of recently developed metaprogramming techniques, this is achieved entirely in pure C++, without introducing a new syntax. Boost.Python's rich set of features and high-level interface make it possible to engineer packages from the ground up as hybrid systems, giving programmers easy and coherent access to both the efficient compile-time polymorphism of C++ and the extremely convenient run-time polymorphism of Python.express

介紹(Introduction)

做爲兩種語言,Python和C++存在不少差別: C++被編譯成機器碼,而Python是解釋執行的。Python的動態類型系統經常被認爲是它靈活性的基礎,而在C++的靜態類型是C++效率的基石。C++有一種複雜艱深的編譯時元語言,而在Python中,幾乎一切都發生在運行時。編程

Python and C++ are in many ways as different as two languages could be: while C++ is usually compiled to machine-code, Python is interpreted. Python's dynamic type system is often cited as the foundation of its flexibility, while in C++ static typing is the cornerstone of its efficiency. C++ has an intricate and difficult compile-time meta-language, while in Python, practically everything happens at runtime.api

然而對不少程序員來講, 這些差別意味着Python和C++能夠完美互補。Python程序的性能瓶頸能夠用C++重寫, 強大的C++庫的做者選擇Python做爲中間件語言, 以實現其靈活的系統集成能力。此外,在表面差別掩蓋之下,兩者有一些很是類似之處:數組

  • C-家族的控制結構(if, while, for…)
  • 支持面向對象、函數式編程和泛型編程(它們都是多範式編程語言)。
  • 認同語法可變性(syntactic variability)對於提升代碼可讀性和表達力的重要做用,提供了對運算符重載的普遍支持。
  • 高級概念,如集合和迭代器。
  • 高級封裝工具(C++:名字空間,Python:模塊)以支持可重用庫的設計。
  • 異常處理,提供有效的錯誤管理。
  • 經常使用的C++慣用法,如handle/body類和引用計數的智能指針反映了Python的引用語義。

Yet for many programmers, these very differences mean that Python and C++ complement one another perfectly. Performance bottlenecks in Python programs can be rewritten in C++ for maximal speed, and authors of powerful C++ libraries choose Python as a middleware language for its flexible system integration capabilities. Furthermore, the surface differences mask some strong similarities:安全

  • 'C'-family control structures (if, while, for...)
  • Support for object-orientation, functional programming, and generic programming (these are both multi-paradigm programming languages.)
  • Comprehensive operator overloading facilities, recognizing the importance of syntactic variability for readability and expressivity.
  • High-level concepts such as collections and iterators.
  • High-level encapsulation facilities (C++: namespaces, Python: modules) to support the design of re-usable libraries.
  • Exception-handling for effective management of error conditions.
  • C++ idioms in common use, such as handle/body classes and reference-counted smart pointers mirror Python reference semantics.

鑑於Python豐富的「C」互操做性API,原則上,向Python導出C++類型和函數接口應該是可行的,並提供與C++對應接口相似的接口。可是,Python單獨提供的用於與C++集成的工具相對較少。與C++和Python相比,「C」只有很是基本的抽象功能,並且徹底不支持異常處理。「C」擴展模塊編寫者須要手工管理Python引用計數,這既煩人又極其容易出錯。傳統的擴展模塊每每包含大量重複的樣板代碼,這使它們難以維護,尤爲是當要封裝的API尚處於發展之中。

Given Python's rich 'C' interoperability API, it should in principle be possible to expose C++ type and function interfaces to Python with an analogous interface to their C++ counterparts. However, the facilities provided by Python alone for integration with C++ are relatively meager. Compared to C++ and Python, 'C' has only very rudimentary abstraction facilities, and support for exception-handling is completely missing. 'C' extension module writers are required to manually manage Python reference counts, which is both annoyingly tedious and extremely error-prone. Traditional extension modules also tend to contain a great deal of boilerplate code repetition which makes them difficult to maintain, especially when wrapping an evolving API.

這些限制致使了各類封裝系統(wrapping systems)的開發。SWIG多是最流行的C/C++和Python集成系統。還有最近發展的SIP,它是專門爲Qt圖形用戶界面庫設計的,用於提供Qt的Python接口。爲了定製語言間的綁定,SWIG和SIP都引入了它們本身的專用語言。這有必定的好處,可是你不得不去應付三種不一樣語言(Python、C/C++和接口語言),因此也帶來了事實上和心理上的困難。CXX軟件包展現了另外一種使人感興趣的選擇。它顯示,至少能夠封裝部分Python 'C' API,將它們表示爲更友好的C++接口。然而,不像SWIG和SIP,CXX不能將C++類封裝成新的Python類型。

These limitations have lead to the development of a variety of wrapping systems. SWIG is probably the most popular package for the integration of C/C++ and Python. A more recent development is SIP, which was specifically designed for interfacing Python with the Qt graphical user interface library. Both SWIG and SIP introduce their own specialized languages for customizing inter-language bindings. This has certain advantages, but having to deal with three different languages (Python, C/C++ and the interface language) also introduces practical and mental difficulties. The CXX package demonstrates an interesting alternative. It shows that at least some parts of Python's 'C' API can be wrapped and presented through a much more user-friendly C++ interface. However, unlike SWIG and SIP, CXX does not include support for wrapping C++ classes as new Python types.

Boost.Python的特性和目標與這些系統有不少重疊。Boost.Python努力提升封裝的便利性和靈活性,但不引入單獨的封裝語言。相反,它經過靜態元編程在幕後管理大量的複雜性,呈現給用戶一個高級C++接口來封裝C++類和函數。Boost.Python也在以下領域超越了早期的系統:

  • 支持C++虛擬函數, 並能在Python中重寫。
  • 對於低級的C++指針和引用,提供全面的生命期管理機制。
  • 支持按Python包組織擴展模塊,經過中心註冊表進行語言間類型轉換。
  • 經過一種安全方便的機制,引入Python強大的序列化引擎(pickle)。
  • 與C++處理左值和右值的規則相一致,該一致性只能來自於對Python和C++類型系統的深刻理解。

The features and goals of Boost.Python overlap significantly with many of these other systems. That said, Boost.Python attempts to maximize convenience and flexibility without introducing a separate wrapping language. Instead, it presents the user with a high-level C++ interface for wrapping C++ classes and functions, managing much of the complexity behind-the-scenes with static metaprogramming. Boost.Python also goes beyond the scope of earlier systems by providing:

  • Support for C++ virtual functions that can be overridden in Python.
  • Comprehensive lifetime management facilities for low-level C++ pointers and references.
  • Support for organizing extensions as Python packages, with a central registry for inter-language type conversions.
  • A safe and convenient mechanism for tying into Python's powerful serialization engine (pickle).
  • Coherence with the rules for handling C++ lvalues and rvalues that can only come from a deep understanding of both the Python and C++ type systems.

一個關鍵性的發現啓動了Boost.Python的開發,即利用C++的編譯時內省,能夠消除傳統擴展模塊中的大量樣板代碼。如每一個封裝的C++函數的參數都是從Python對象提取的,提取時必須根據參數類型調用相應的過程。相似地,函數返回值從C++轉換成Python時,返回值的類型決定了如何轉換。由於參數和返回值的類型是每一個函數類型的一部分,因此Boost.Python能夠從函數類型推導出大部分所需的信息。

The key insight that sparked the development of Boost.Python is that much of the boilerplate code in traditional extension modules could be eliminated using C++ compile-time introspection. Each argument of a wrapped C++ function must be extracted from a Python object using a procedure that depends on the argument type. Similarly the function's return type determines how the return value will be converted from C++ to Python. Of course argument and return types are part of each function's type, and this is exactly the source from which Boost.Python deduces most of the information required.

這種方法致使了「用戶指導的封裝(user guided wrapping)」:在純C++的框架內,從待封裝的源代碼中直接提取儘量多的信息,而一些額外的信息由用戶顯式提供。一般這種指導是自動的,不多須要真正的干涉。由於接口規範和導出代碼是用同一門全功能的語言寫的,當用戶確實須要取得控制時,他所擁有的權力是空前強大的。

This approach leads to user guided wrapping: as much information is extracted directly from the source code to be wrapped as is possible within the framework of pure C++, and some additional information is supplied explicitly by the user. Mostly the guidance is mechanical and little real intervention is required. Because the interface specification is written in the same full-featured language as the code being exposed, the user has unprecedented power available when she does need to take control.

設計目標 (Boost.Python Design Goals)

  1. 可以從Python直接操做C++對象;
  2. 必須可以消除語言接口的差別;
  3. 避免C++崩潰和用更健壯的替代方案來替代容易出錯的「C」接口;
  4. 支持基於組件的開發;
  5. 封裝必須是非侵入的, 第三方必須可以封裝現有的C++庫,即便他只有頭文件和二進制庫。

Boost.Python的首要目標是,讓用戶只用C++編譯器就能向Python導出C++類和函數。總的來講,用戶體驗應該是,可以從Python直接操做C++對象。

The primary goal of Boost.Python is to allow users to expose C++ classes and functions to Python using nothing more than a C++ compiler. In broad strokes, the user experience should be one of directly manipulating C++ objects from Python.

然而,有一點也很重要,那就是不要過於按字面翻譯全部接口:必須考慮每種語言的慣用法。例如,雖然C++和Python都有迭代器的概念,表達方式卻很不同。Boost.Python必須可以消除這種接口的差別。

However, it's also important not to translate all interfaces too literally: the idioms of each language must be respected. For example, though C++ and Python both have an iterator concept, they are expressed very differently. Boost.Python has to be able to bridge the interface gap.

Python用戶可能會誤用C++接口,所以,Boost.Python必須可以隔離因輕微的誤用而形成的崩潰,例如訪問已刪除的對象。一樣的,Boost.Python庫應該把C++用戶從低級的Python 'C' API中解放出來,將容易出錯的'C'接口,如手工引用計數管理、原始的PyObject指針,替換爲更健壯的接口。

It must be possible to insulate Python users from crashes resulting from trivial misuses of C++ interfaces, such as accessing already-deleted objects. By the same token the library should insulate C++ users from low-level Python 'C' API, replacing error-prone 'C' interfaces like manual reference-count management and raw PyObject pointers with more-robust alternatives.

支持基於組件的開發是相當重要的,這樣,一個擴展模塊導出的C++類型,能夠傳遞給另外一個模塊導出的函數,而不丟失重要的信息,好比C++的繼承關係。

Support for component-based development is crucial, so that C++ types exposed in one extension module can be passed to functions exposed in another without loss of crucial information like C++ inheritance relationships.

最後,全部的封裝必須是非侵入性的(non-intrusive),不能修改最初的C++源碼,甚至沒必要看到源碼。第三方必須可以封裝現有的C++庫,即便他只有頭文件和二進制庫。

Finally, all wrapping must be non-intrusive, without modifying or even seeing the original C++ source code. Existing C++ libraries have to be wrappable by third parties who only have access to header files and binaries.

Hello Boost.Python World

如今來預覽一下Boost.Python,看看它是如何改進Python原有的封裝機制的。下面是咱們想導出的函數:

And now for a preview of Boost.Python, and how it improves on the raw facilities offered by Python. Here's a function we might want to expose:

char const* greet(unsigned x)
{
   static char const* const msgs[] = { "hello", "Boost.Python", "world!" };

   if (x > 2)
       throw std::range_error("greet: index out of range");

   return msgs[x];
}

在標準C++中,用Python 'C' API來封裝這個函數,咱們須要像這樣作:

To wrap this function in standard C++ using the Python 'C' API, we'd need something like this:

extern "C" // all Python interactions use 'C' linkage and calling convention
{
    // Wrapper to handle argument/result conversion and checking
    PyObject* greet_wrap(PyObject* args, PyObject * keywords)
    {
         int x;
         if (PyArg_ParseTuple(args, "i", &x))    // extract/check arguments
         {
             char const* result = greet(x);      // invoke wrapped function
             return PyString_FromString(result); // convert result to Python
         }
         return 0;                               // error occurred
    }

    // Table of wrapped functions to be exposed by the module
    static PyMethodDef methods[] = {
        { "greet", greet_wrap, METH_VARARGS, "return one of 3 parts of a greeting" }
        , { NULL, NULL, 0, NULL } // sentinel
    };

    // module initialization function
    DL_EXPORT init_hello()
    {
        (void) Py_InitModule("hello", methods); // add the methods to the module
    }
}

下面是咱們用Boost.Python來導出的封裝代碼:
Now here's the wrapping code we'd use to expose it with Boost.Python:

#include <boost/python.hpp>
using namespace boost::python;
BOOST_PYTHON_MODULE(hello)
{
    def("greet", greet, "return one of 3 parts of a greeting");
}

下面是運行結果:
and here it is in action:

>>> import hello
>>> for x in range(3):
...     print hello.greet(x)
...
hello
Boost.Python
world!

使用'C' API的版本要冗長的多,此外,還須要注意,有些東西它沒有正確處理:

  • 錯誤值傳遞. 原來的函數接受一個無符號整數, 而Python的 'C' API只給咱們一種提取帶符號整數的方法。若是咱們試着傳遞一個負數到hello.greet, Boost.Python版本將會引起一個Python異常; 可是另外一個('C'API版)將繼續執行C++實現, 並將負整數轉換爲無符號整數(一般會變爲某個很是大的數),而後把不正確的轉換結果傳遞給被封裝的函數。
  • 這給咱們帶來了第二個問題: 若是C++ greet()函數使用大於2的參數, 它將拋出一個異常。典型的,若是C++異常傳播時,跨越了'C'編譯器生成的代碼的邊界,就會致使崩潰. 正如你在第一個版本中所見,那兒沒有防止崩潰的C++機制。而Boost.Python封裝的函數自動包含了異常處理層,它把未處理的C++異常翻譯成相應的Python異常,從而保護了Python用戶。
  • 一個更微妙的限制是,在Python「C」API案例中使用的參數轉換隻能以一種方式得到整數xPyArg_ParseTuple不能進行下面的轉換:若是有一個Python的long(任意精度的整數)對象,它的大小正好屬於unsigned int,但不屬於signed long。它也不能處理用戶自定義隱式轉換operator unsigned int()的C++封裝類. Boost.Python的動態類型轉換註冊表容許用戶添加任意轉換方法。

Aside from the fact that the 'C' API version is much more verbose, it's worth noting a few things that it doesn't handle correctly:

  • The original function accepts an unsigned integer, and the Python 'C' API only gives us a way of extracting signed integers. The Boost.Python version will raise a Python exception if we try to pass a negative number to hello.greet, but the other one will proceed to do whatever the C++ implementation does when converting an negative integer to unsigned (usually wrapping to some very large number), and pass the incorrect translation on to the wrapped function.
  • That brings us to the second problem: if the C++ greet() function is called with a number greater than 2, it will throw an exception. Typically, if a C++ exception propagates across the boundary with code generated by a 'C' compiler, it will cause a crash. As you can see in the first version, there's no C++ scaffolding there to prevent this from happening. Functions wrapped by Boost.Python automatically include an exception-handling layer which protects Python users by translating unhandled C++ exceptions into a corresponding Python exception.
  • A slightly more-subtle limitation is that the argument conversion used in the Python 'C' API case can only get that integer x in one way. PyArg_ParseTuple can't convert Python long objects (arbitrary-precision integers) which happen to fit in an unsigned int but not in a signed long, nor will it ever handle a wrapped C++ class with a user-defined implicit operator unsigned int() conversion. Boost.Python's dynamic type conversion registry allows users to add arbitrary conversion methods.

庫概述 (Library Overview)

本節簡述了庫的一些主要特性。在不影響理解的狀況下,省略了庫的實現細節。

This section outlines some of the library's major features. Except as neccessary to avoid confusion, details of library implementation are omitted.

導出類 (Exposing Classes)

C++類和結構是用一樣簡潔的接口導出的。若有:
C++ classes and structs are exposed with a similarly-terse interface. Given:

struct World
{
    void set(std::string msg) { this->msg = msg; }
    std::string greet() { return msg; }
    std::string msg;
};

如下代碼會將它導出到擴展模塊:
The following code will expose it in our extension module:

#include <boost/python.hpp>
BOOST_PYTHON_MODULE(hello)
{
    class_<World>("World")
        .def("greet", &World::greet)
        .def("set", &World::set)
    ;
}

儘管上述代碼具備某種熟悉的Python風格,但語法仍是有點使人迷惑,由於它看起來不像一般的C++代碼。可是,這仍然是正確的標準C++。由於C++和Python具備靈活的語法和運算符重載,它們都很善於定義特定領域(子)語言(DSLs, domain-specific (sub)languages)。咱們在Boost.Python裏面就是定義了一個DSL。把代碼拆開來看:

Although this code has a certain pythonic familiarity, people sometimes find the syntax bit confusing because it doesn't look like most of the C++ code they're used to. All the same, this is just standard C++. Because of their flexible syntax and operator overloading, C++ and Python are great for defining domain-specific (sub)languages (DSLs), and that's what we've done in Boost.Python. To break it down:

class_<World>("World")

構造一個class_<World>類型的匿名對象,並將「World」傳遞給它的構造函數。這將在擴展模塊中建立一個名爲World的新型Python類,並在Boost.Python的類型轉換註冊表裏,把它和C++類型World關聯起來。咱們也能夠這樣寫:

constructs an unnamed object of type class_ and passes "World" to its constructor. This creates a new-style Python class called World in the extension module, and associates it with the C++ type World in the Boost.Python type conversion registry. We might have also written:

class_<World> w("World");

可是那樣會更冗長,由於咱們必須再次命名w來調用它的def()成員函數:

but that would've been more verbose, since we'd have to name w again to invoke its def() member function:

w.def("greet", &World::greet)

鏈式操做*, 在最初的示例中,點表示成員訪問,它的位置沒有什麼特別的: C++容許標記(token)的兩邊能夠有任意數量的空白符。把點放在每行的開始,可讓咱們用統一的句法,鏈式串聯連續的成員函數調用,想串多少都行。容許鏈式調用的另外一個關鍵事實是,class_<>成員函數都返回對*this的引用。

There's nothing special about the location of the dot for member access in the original example: C++ allows any amount of whitespace on either side of a token, and placing the dot at the beginning of each line allows us to chain as many successive calls to member functions as we like with a uniform syntax. The other key fact that allows chaining is that class_<> member functions all return a reference to *this.

因此本例等價於:

So the example is equivalent to:

class_<World> w("World");
w.def("greet", &World::greet);
w.def("set", &World::set);

這種方式將Boost.Python類封裝的部件都拆分開來了,這樣拆分有時候是有用的。可是本文的其他部分將堅持使用簡潔格式。

It's occasionally useful to be able to break down the components of a Boost.Python class wrapper in this way, but the rest of this article will stick to the terse syntax.

最後,來看封裝類的使用:

For completeness, here's the wrapped class in use:

>>> import hello
>>> planet = hello.World()
>>> planet.set('howdy')
>>> planet.greet()
'howdy'

構造函數 Constructors

因爲咱們的World類只是一個普通的struct,它有一個隱式無參數(null)構造函數。Boost.Python默認會導出這個無參數構造函數,因此咱們能夠這樣寫:

Since our World class is just a plain struct, it has an implicit no-argument (nullary) constructor. Boost.Python exposes the nullary constructor by default, which is why we were able to write:

>>> planet = hello.World()

然而,在任何語言裏,對於設計良好的類,構造函數可能須要參數,以創建類的不變式(invariant)。在Python中__init__只是一個特殊命名的方法,而C++的構造函數與Python不一樣,它不能像普通成員函數那樣處理。特別是,咱們不能取它的地址: &World::World是一個錯誤。Boost.Python庫提供了一個不一樣的接口來指定構造函數. 假設有:

However, well-designed classes in any language may require constructor arguments in order to establish their invariants. Unlike Python, where init is just a specially-named method, In C++ constructors cannot be handled like ordinary member functions. In particular, we can't take their address: &World::World is an error. The library provides a different interface for specifying constructors. Given:

struct World
{
    World(std::string msg); // added constructor
    ...

咱們能夠修改咱們的封裝代碼以下:
we can modify our wrapping code as follows:

class_<World>("World", init<std::string>())
    ...

固然,C++類可能還有其餘的構造函數,咱們也能夠導出它們,只須要向def()傳入更多的init<…>實例:
of course, a C++ class may have additional constructors, and we can expose those as well by passing more instances of init<...> to def():

class_<World>("World", init<std::string>())
    .def(init<double, double>())
    ...

Boost.Python封裝的函數、成員函數,以及構造函數均可以重載,以映射C++中的重載。
Boost.Python allows wrapped functions, member functions, and constructors to be overloaded to mirror C++ overloading.

數據成員和屬性 Data Members and Properties

C++類中任何可公開訪問的數據成員均可以很容易地導出爲只讀或讀寫屬性:
Any publicly-accessible data members in a C++ class can be easily exposed as either readonly or readwrite attributes:

class_<World>("World", init<std::string>())
    .def_readonly("msg", &World::msg)
    ...

而且能夠直接在Python中使用:
and can be used directly in Python:

>>> planet = hello.World('howdy')
>>> planet.msg
'howdy'

這不會在World實例__dict__中添加屬性,從而在封裝大型數據結構時能節省大量內存。實際上,除非從Python中顯式地添加屬性,不然根本不會建立實例__dict__。Boost.Python將這種功能歸功於新的Python 2.2類型系統,特別是描述符接口和property類型。

This does not result in adding attributes to the World instance __dict__, which can result in substantial memory savings when wrapping large data structures. In fact, no instance __dict__ will be created at all unless attributes are explicitly added from Python. Boost.Python owes this capability to the new Python 2.2 type system, in particular the descriptor interface and property type.

在C++中,可公開訪問的數據成員是糟糕設計的標誌,由於它們破壞了封裝性,而且風格指南一般指示使用「getter」和「setter」函數。然而在Python中,__getattr____setattr__和從2.2起出現的property意味着屬性訪問是一個程序員可用的,封裝性更好的語法工具。Boost.Python經過讓用戶能夠直接建立Python property,從而消除了兩者語言習慣上的差別。即便msg是私有的,咱們仍然能夠將它做爲屬性在Python中導出,以下所示:

In C++, publicly-accessible data members are considered a sign of poor design because they break encapsulation, and style guides usually dictate the use of "getter" and "setter" functions instead. In Python, however, __getattr__, __setattr__, and since 2.2, property mean that attribute access is just one more well-encapsulated syntactic tool at the programmer's disposal. Boost.Python bridges this idiomatic gap by making Python property creation directly available to users. If msg were private, we could still expose it as attribute in Python as follows:

class_<World>("World", init<std::string>())
    .add_property("msg", &World::greet, &World::set)
    ...

上例等同於Python 2.2+裏面熟悉的屬性用法:
The example above mirrors the familiar usage of properties in Python 2.2+:

class World(object):
    __init__(self, msg):
        self.__msg = msg
    def greet(self):
        return self.__msg
    def set(self, msg):
        self.__msg = msg
    msg = property(greet, set)

運算符重載 Operator Overloading

兩種語言都可以爲用戶自定義類型編寫算術運算符,這是它們在數值計算上得到成功的主要因素,而像NumPy這樣的軟件包的成功證實了在擴展模塊中導出運算符的威力。Boost.Python爲封裝運算符重載提供了一種簡潔的機制。下面是Boost有理數庫封裝代碼的片段:

The ability to write arithmetic operators for user-defined types has been a major factor in the success of both languages for numerical computation, and the success of packages like NumPy attests to the power of exposing operators in extension modules. Boost.Python provides a concise mechanism for wrapping operator overloads. The example below shows a fragment from a wrapper for the Boost rational number library:

class_<rational<int> >("rational_int")
  .def(init<int, int>()) // constructor, e.g. rational_int(3, 4)
  .def("numerator", &rational<int>::numerator)
  .def("denominator", &rational<int>::denominator)
  .def(-self)        // __neg__ (unary minus)
  .def(self + self)  // __add__ (homogeneous)
  .def(self * self)  // __mul__
  .def(self + int()) // __add__ (heterogenous)
  .def(int() + self) // __radd__
  ...

魔法的施展只是簡單應用了「表達式模板」[VELD1995],該技術最初是爲優化高性能矩陣代數表達式而開發的。其精髓是,不是當即進行計算,而是重載來構造一個表明計算的類型。在矩陣代數中,當考慮整個表達式的結構,而不是「貪婪地」計算每一個操做時,常常能夠得到顯著的優化。Boost.Python使用了相同的技術,它用包含self的表達式,構建了一個適當的Python成員方法對象。

The magic is performed using a simplified application of "expression templates" [VELD1995], a technique originally developed for optimization of high-performance matrix algebra expressions. The essence is that instead of performing the computation immediately, operators are overloaded to construct a type representing the computation. In matrix algebra, dramatic optimizations are often available when the structure of an entire expression can be taken into account, rather than evaluating each operation "greedily". Boost.Python uses the same technique to build an appropriate Python method object based on expressions involving self.

繼承 Inheritance

要在Boost.Python裏描述C++繼承關係,能夠在class_<…>模板參數列表中添加一個可選的bases<…>參數, 以下:

C++ inheritance relationships can be represented to Boost.Python by adding an optional bases<...> argument to the class_<...> template parameter list as follows:

class_<Derived, bases<Base1, Base2> >("Derived")
     ...

這有兩個做用:

  • class_<……>建立時,會在Boost.Python的類型對象註冊表中查找與Base1Base2對應的Python類型對象,並用做新的PythonDerived類型對象的基類,所以,爲Python Base1Base2類型導出的成員函數將自動成爲Derived類型的成員。由於註冊表是全局的,因此Derived和它的基類能夠在不一樣的模塊中導出。
  • 在Boost.Python的註冊表裏,添加了從Derived到它的基類的C++轉換。這樣,封裝了Derived實例的對象就能夠調用其基類的方法,而該封裝的C++方法本該由一個基類對象(指針或引用)來調用。類T的成員方法封裝後,可視爲它們具備一個隱含的第一參數T&,因此爲了容許派生類對象調用基類方法,這些轉換是必須的。

This has two effects:

  • When the class_<...> is created, Python type objects corresponding to Base1 and Base2 are looked up in Boost.Python's registry, and are used as bases for the new Python Derived type object, so methods exposed for the Python Base1 and Base2 types are automatically members of the Derived type. Because the registry is global, this works correctly even if Derived is exposed in a different module from either of its bases.
  • C++ conversions from Derived to its bases are added to the Boost.Python registry. Thus wrapped C++ methods expecting (a pointer or reference to) an object of either base type can be called with an object wrapping a Derived instance. Wrapped member functions of class T are treated as though they have an implicit first argument of T&, so these conversions are neccessary to allow the base class methods to be called for derived objects.

固然,能夠從封裝好的C++類派生新的Python類。由於Boost.Python使用了新型的類系統,從封裝類派生就像是從Python內置類型派生同樣。但有一個重大區別:內置類型通常在__new__函數裏創建不變式,所以其派生類不須要調用基類的__init__:

Of course it's possible to derive new Python classes from wrapped C++ class instances. Because Boost.Python uses the new-style class system, that works very much as for the Python built-in types. There is one significant detail in which it differs: the built-in types generally establish their invariants in their __new__ function, so that derived classes do not need to call __init__ on the base class before invoking its methods :

>>> class L(list):
...      def __init__(self):
...          pass
...
>>> L().reverse()
>>>

因爲C++對象構造是一個單步操做,在__init__函數中,只有參數齊全,才能構造C++實例數據:

Because C++ object construction is a one-step operation, C++ instance data cannot be constructed until the arguments are available, in the __init__ function:

>>> class D(SomeBoostPythonClass):
...      def __init__(self):
...          pass
...
>>> D().some_boost_python_method()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: bad argument type for built-in operation

這種狀況發生是由於Boost.Python在D實例中沒法找到SomeBoostPythonClass實例數據; D的__init__函數遮蓋了基類的構造函數。糾正方法爲,刪除D的__init__函數,或顯示調用SomeBoostPythonClass.__init__(…)

This happened because Boost.Python couldn't find instance data of type SomeBoostPythonClass within the D instance; D's __init__ function masked construction of the base class. It could be corrected by either removing D's __init__ function or having it call SomeBoostPythonClass.__init__(...) explicitly.

虛函數 Virtual Functions

用Python從擴展類派生新的類型沒有太大意思,除非能夠在C++中以多地使用派生類。換句話說,在C++裏,經過基類指針或引用調用C++虛函數時,Python實現的方法應該看起來像是覆蓋了C++虛函數的實現。由於改變虛函數行爲的惟一方法是,在派生類裏覆蓋它,因此用戶必須構建一個特殊的派生類,來分派多態類的虛函數:

Deriving new types in Python from extension classes is not very interesting unless they can be used polymorphically from C++. In other words, Python method implementations should appear to override the implementation of C++ virtual functions when called through base class pointers/references from C++. Since the only way to alter the behavior of a virtual function is to override it in a derived class, the user must build a special derived class to dispatch a polymorphic class' virtual functions:

//
// interface to wrap:
//
class Base
{
 public:
    virtual int f(std::string x) { return 42; }
    virtual ~Base();
};

int calls_f(Base const& b, std::string x) { return b.f(x); }

//
// Wrapping Code
//

// Dispatcher class
struct BaseWrap : Base
{
    // Store a pointer to the Python object
    BaseWrap(PyObject* self_) : self(self_) {}
    PyObject* self;

    // Default implementation, for when f is not overridden
    int f_default(std::string x) { return this->Base::f(x); }
    // Dispatch implementation
    int f(std::string x) { return call_method<int>(self, "f", x); }
};

...
    def("calls_f", calls_f);
    class_<Base, BaseWrap>("Base")
        .def("f", &Base::f, &BaseWrap::f_default)
        ;

這是Python演示代碼:
Now here's some Python code which demonstrates:

>>> class Derived(Base):
...     def f(self, s):
...          return len(s)
...
>>> calls_f(Base(), 'foo')
42
>>> calls_f(Derived(), 'forty-two')
9

關於dispatcher類須要注意的事項:

  • 在Python中容許覆蓋的關鍵因素是call_method調用,與C++函數封裝同樣,它使用同一個全局註冊表,把參數從C++轉換爲Python,並將返回類型從Python轉換爲C++。
  • 任何但願封裝的任何構造函數,其函數簽名必須有一個的相同的初始化參數PyObject*
  • dispatcher必須保存這個參數,以即可以使用它調用call_method
  • 當導出的函數不是純虛函數時,須要使用f_default成員函數;在BaseWrap類型的對象上,不能調用Base::f,由於f被覆蓋了。

Things to notice about the dispatcher class:

  • The key element which allows overriding in Python is the call_method invocation, which uses the same global type conversion registry as the C++ function wrapping does to convert its arguments from C++ to Python and its return type from Python to C++.
  • Any constructor signatures you wish to wrap must be replicated with an initial PyObject* argument
  • The dispatcher must store this argument so that it can be used to invoke call_method
  • The f_default member function is needed when the function being exposed is not pure virtual; there's no other way Base::f can be called on an object of type BaseWrap, since it overrides f.

更深的反射即將出現? Deeper Reflection on the Horizon?

無能否認,重複這種公式化動做是冗長乏味的,尤爲是項目裏有大量多態類的時候。這裏有必要反映一些C++編譯時內省能力的限制:C++沒法列舉類的成員並找出虛函數。不過,至少有一個項目已經啓動,有但願編寫出一個前端程序,能夠從C++頭文件自動生成這些分派類(和其餘封裝代碼),

Admittedly, this formula is tedious to repeat, especially on a project with many polymorphic classes. That it is neccessary reflects some limitations in C++'s compile-time introspection capabilities: there's no way to enumerate the members of a class and find out which are virtual functions. At least one very promising project has been started to write a front-end which can generate these dispatchers (and other wrapping code) automatically from C++ headers.

Bruno da Silva de Oliveira正在開發Pyste。Pyste基於GCC_XML構建,而GCC_XML能夠生成XML版本的GCC內部程序描述。由於GCC是一種高度兼容標準的C++編譯器,從而確保了對最複雜的模板代碼的正確處理,和對底層類型系統的徹底訪問。和Boost.Python的哲學一致,Pyste接口描述既不侵入待封裝的代碼,也不使用某種不熟悉的語言來表達,相反,它是100%的純Python腳本。若是Pyste成功的話,它將標誌,咱們的許多用戶沒必要直接用C++封裝全部東西。Pyste也將容許咱們選擇性地把一些元編程代碼從C++轉移到Python。咱們期待不久之後,不只用戶,並且Boost.Python開發者也能,「混合地思考」他們本身的代碼。(譯註:Pyste已再也不維護,更新的是Py++。)

Pyste is being developed by Bruno da Silva de Oliveira. It builds on GCC_XML, which generates an XML version of GCC's internal program representation. Since GCC is a highly-conformant C++ compiler, this ensures correct handling of the most-sophisticated template code and full access to the underlying type system. In keeping with the Boost.Python philosophy, a Pyste interface description is neither intrusive on the code being wrapped, nor expressed in some unfamiliar language: instead it is a 100% pure Python script. If Pyste is successful it will mark a move away from wrapping everything directly in C++ for many of our users. It will also allow us the choice to shift some of the metaprogram code from C++ to Python. We expect that soon, not only our users but the Boost.Python developers themselves will be "thinking hybrid" about their own code.

序列化 Serialization

序列化(serialization)是,將內存中的對象轉換爲能夠保存格式,使之保存到磁盤上或經過網絡鏈接發送的過程。序列化的對象(一般是純字符串)能夠恢復並轉換回原來的對象。一個好的序列化系統將自動轉換整個對象層次結構。Python的標準pickle模塊就是這樣一個系統。它利用該語言強大的運行時自省功能來序列化實際任意用戶定義的對象。經過一些簡單和無干擾的規定,這個強大的機制能夠擴展到也適用於封裝的C++對象。下面是一個例子:

Serialization is the process of converting objects in memory to a form that can be stored on disk or sent over a network connection. The serialized object (most often a plain string) can be retrieved and converted back to the original object. A good serialization system will automatically convert entire object hierarchies. Python's standard pickle module is just such a system. It leverages the language's strong runtime introspection facilities for serializing practically arbitrary user-defined objects. With a few simple and unintrusive provisions this powerful machinery can be extended to also work for wrapped C++ objects. Here is an example:

#include <string>

struct World
{
    World(std::string a_msg) : msg(a_msg) {}
    std::string greet() const { return msg; }
    std::string msg;
};

#include <boost/python.hpp>
using namespace boost::python;

struct World_picklers : pickle_suite
{
  static tuple
  getinitargs(World const& w) { return make_tuple(w.greet()); }
};

BOOST_PYTHON_MODULE(hello)
{
    class_<World>("World", init<std::string>())
        .def("greet", &World::greet)
        .def_pickle(World_picklers())
    ;
}

如今讓咱們建立一個World對象,並把它保存在磁盤上:
Now let's create a World object and put it to rest on disk:

>>> import hello
>>> import pickle
>>> a_world = hello.World("howdy")
>>> pickle.dump(a_world, open("my_world", "w"))
:todo ?不理解

而後,多是在不一樣的計算機上不一樣的操做系統的不一樣的一個腳本上,咱們這樣用:???
In a potentially different script on a potentially different computer with a potentially different operating system:

>>> import pickle
>>> resurrected_world = pickle.load(open("my_world", "r"))
>>> resurrected_world.greet()
'howdy'

固然,cPickle模塊也能夠用於更快的處理。
Of course the cPickle module can also be used for faster processing.

Boost.Python的pickle_suite徹底支持標準Python文檔中定義的pickle協議。就像Python中的__getinitargs__函數同樣,pickle_suite的getinitargs()負責建立參數元組,以重建pickle的對象。Python pickle協議的其餘元素__getstate____setstate__能夠經過C++ getstatesetstate函數選擇性地提供。利用C++的靜態類型系統,Boost.Python庫在編譯時保證,不會使用沒有意義的函數組合(例如,有getstate無setstate)。

Boost.Python's pickle_suite fully supports the pickle protocol defined in the standard Python documentation. Like a __getinitargs__ function in Python, the pickle_suite's getinitargs() is responsible for creating the argument tuple that will be use to reconstruct the pickled object. The other elements of the Python pickling protocol, __getstate__ and __setstate__ can be optionally provided via C++ getstate and setstate functions. C++'s static type system allows the library to ensure at compile-time that nonsensical combinations of functions (e.g. getstate without setstate) are not used.

要想序列化更復雜的C++對象,就須要作更多的工做。幸運的是,object接口(見下一節)幫了大忙,它保持了代碼的可管理性。

Enabling serialization of more complex C++ objects requires a little more work than is shown in the example above. Fortunately the object interface (see next section) greatly helps in keeping the code manageable.

對象接口 Object interface

對於有經驗的'C'語言擴展模塊的做者,他們應該熟悉無所不在的PyObject*、手動引用計數,以及須要記住哪一個API調用返回「新的」(擁有的)引用或「借來的」(原始的)引用。這些約束不只麻煩,並且是錯誤的主要來源,特別是在出現異常的狀況下。

Experienced 'C' language extension module authors will be familiar with the ubiquitous PyObject*, manual reference-counting, and the need to remember which API calls return "new" (owned) references or "borrowed" (raw) references. These constraints are not just cumbersome but also a major source of errors, especially in the presence of exceptions.

Boost.Python提供了一個object類,它自動化引用計數,而且能把任意類型的C++對象轉換到Python。對於將來的擴展模塊的編寫者來講,這極大地減輕了學習的負擔。

Boost.Python provides a class object which automates reference counting and provides conversion to Python from C++ objects of arbitrary type. This significantly reduces the learning effort for prospective extension module writers.

從任何其餘類型建立object是很是簡單的:

Creating an object from any other type is extremely simple:

object s("hello, world");  // s manages a Python string

object和全部其餘類型的交互,以及到Python的自動轉換,都已經模板化了。這一切進行得如此天然,以致於能夠輕鬆地忽略掉它:

object has templated interactions with all other types, with automatic to-python conversions. It happens so naturally that it's easily overlooked:

object ten_Os = 10 * s[4]; // -> "oooooooooo"

在上面的示例中,在調用索引和乘法操做以前,4和10被轉換爲Python對象。

In the example above, 4 and 10 are converted to Python objects before the indexing and multiplication operations are invoked.

extract<T>能夠用來將Python對象轉換爲C++類型:
The extract class template can be used to convert Python objects to C++ types:

double x = extract<double>(o);

若是有一個方向的轉換不能進行,則將在運行時拋出一個適當的異常。
If a conversion in either direction cannot be performed, an appropriate exception is thrown at runtime.

除了object類型,還有一組派生類型,它們儘量多地對應Python內置類型,如list、dict、tuple等。這樣就能方便地從C++操做這些高級類型了:

The object type is accompanied by a set of derived types that mirror the Python built-in types such as list, dict, tuple, etc. as much as possible. This enables convenient manipulation of these high-level types from C++:

dict d;
d["some"] = "thing";
d["lucky_number"] = 13;
list l = d.keys();

這看起來和工做起來都很像普通的Python代碼,但它是純C++的。固然,咱們也能夠封裝接受或返回object實例的C++函數。
This almost looks and works like regular Python code, but it is pure C++. Of course we can wrap C++ functions which accept or return object instances.

考慮混合編程 Thinking hybrid

由於混合語言編程具備事實上和心理上的困難,因此普通的作法是,在任何開發活動開始時,先肯定一種單一語言。不幸的是,因爲靜態類型系統的複雜性,爲了運行時的性能,咱們所付出的代價經常是,開發時間大大增長。經驗代表,和開發同等的Python代碼相比,編寫可維護的C++代碼一般須要更長的時間,而且要求多得多的來之不易的工做經驗。即便開發者以爲只用一門編譯性語言挺好,爲了用戶的利益,他們也常常給他們的系統增長某種專門的腳本層,由於他們的用戶能夠得到一樣的使用腳本語言的好處。

Because of the practical and mental difficulties of combining programming languages, it is common to settle a single language at the outset of any development effort. For many applications, performance considerations dictate the use of a compiled language for the core algorithms. Unfortunately, due to the complexity of the static type system, the price we pay for runtime performance is often a significant increase in development time. Experience shows that writing maintainable C++ code usually takes longer and requires far more hard-earned working experience than developing comparable Python code. Even when developers are comfortable working exclusively in compiled languages, they often augment their systems by some type of ad hoc scripting layer for the benefit of their users without ever availing themselves of the same advantages.

Boost.Python使咱們可以混合思考。Python能夠用於快速原型化一個新的應用程序;它的易用性和大量的標準庫使咱們在通往工做系統的道路上領先一步。若是須要,可使用工做代碼來發現限速熱點。爲了最大限度地提升性能,這些能夠在C++中從新實現,並使用Boost.Python綁定,並提供給現有的高級過程調用。

Boost.Python enables us to think hybrid. Python can be used for rapidly prototyping a new application; its ease of use and the large pool of standard libraries give us a head start on the way to a working system. If necessary, the working code can be used to discover rate-limiting hotspots. To maximize performance these can be reimplemented in C++, together with the Boost.Python bindings needed to tie them back into the existing higher-level procedure.

固然,若是從一開始就很清楚,許多算法最終將不得不用C++實現,那麼這種自上向下(top-down)的方法就不那麼有吸引力了。幸運的是,Boost.Python還容許咱們採用自下向上(bottom-up)的方法。咱們曾經很是成功地應用這種方法,開發一個科學軟件工具箱。開始的時候,這個工具箱主要是一個C++類庫,並帶有Boost.Python綁定,而且有一段時間,其成長主要集中在C++的部分。然而,當工具箱愈來愈完善,愈來愈多的新增功能能夠用Python實現。

Of course, this top-down approach is less attractive if it is clear from the start that many algorithms will eventually have to be implemented in C++. Fortunately Boost.Python also enables us to pursue a bottom-up approach. We have used this approach very successfully in the development of a toolbox for scientific applications. The toolbox started out mainly as a library of C++ classes with Boost.Python bindings, and for a while the growth was mainly concentrated on the C++ parts. However, as the toolbox is becoming more complete, more and more newly added functionality can be implemented in Python.

算法實現

該圖顯示,實現新的算法時,估計新增C++和Python代碼的比率隨時間的變化。咱們預計這個比率會在接近70%的Python處變平。可以主要地用Python來解決新問題,而不是用更困難的靜態類型語言,這是咱們在Boost.Python上投入的回報。咱們的全部代碼都能從Python訪問,這使得更多的開發者能夠用它來快速開發新的應用。

This figure shows the estimated ratio of newly added C++ and Python code over time as new algorithms are implemented. We expect this ratio to level out near 70% Python. Being able to solve new problems mostly in Python rather than a more difficult statically typed language is the return on our investment in Boost.Python. The ability to access all of our code from Python allows a broader group of developers to use it in the rapid development of new applications.

開發歷史 Development history

Boost.Python的初版是由Dragon Systems的Dave Abrahams在2000年開發的,在Dragon Systems,Dave有幸由Tim Peters引導,接受了「Python之禪(The Zen of Python)」。Dave的工做之一是,開發基於Python的天然語言處理系統(NLP,natural language processing)。因爲最終要用於嵌入式硬件,因此老是假設,計算密集的內核將會用C++來重寫,以優化速度和內存佔用。這個項目也想用Python測試腳原本測試全部的C++代碼。當時,咱們所知的綁定C++和Python的惟一工具是SWIG,但那時它處理C++的能力比較弱。若是說在那時就有什麼深知卓見,說Boost.Python的方法會有何等優越性,那是騙人的。那時,Dave正好對花俏的C++模板技巧感興趣,而且嫺熟到恰好能真正作點東西,Boost.Python就那樣出現了,由於它知足了需求,由於它看起來挺酷,值得一試。

The first version of Boost.Python was developed in 2000 by Dave Abrahams at Dragon Systems, where he was privileged to have Tim Peters as a guide to "The Zen of Python". One of Dave's jobs was to develop a Python-based natural language processing system. Since it was eventually going to be targeting embedded hardware, it was always assumed that the compute-intensive core would be rewritten in C++ to optimize speed and memory footprint1. The project also wanted to test all of its C++ code using Python test scripts2. The only tool we knew of for binding C++ and Python was SWIG, and at the time its handling of C++ was weak. It would be false to claim any deep insight into the possible advantages of Boost.Python's approach at this point. Dave's interest and expertise in fancy C++ template tricks had just reached the point where he could do some real damage, and Boost.Python emerged as it did because it filled a need and because it seemed like a cool thing to try.

這個早期版本針對的目標,與咱們在本文所述的許多基本目標相同,最顯著的區別在於,語法要稍微麻煩一點,而且,對運算符重載、pickling,和基於組件的開發缺少專門的支持。後面這三個特性很快就由Ullrich Koethe和Ralf Grosse-Kunstleve加上了,而且,其餘熱心的貢獻者也出現了,並做了一些改進,如對嵌套模塊和靜態成員函數的支持等。

This early version was aimed at many of the same basic goals we've described in this paper, differing most-noticeably by having a slightly more cumbersome syntax and by lack of special support for operator overloading, pickling, and component-based development. These last three features were quickly added by Ullrich Koethe and Ralf Grosse-Kunstleve3, and other enthusiastic contributors arrived on the scene to contribute enhancements like support for nested modules and static member functions.

到2001年初,開發已經穩定下來了,不多有新增特性了,然而,這時出現了一件新的麻煩事:Ralf在一個使用EDG前端的編譯器的預發佈版上測試Boost.Python,他發現,Boost.Python內核中,Python和C++類型轉換機制沒法經過編譯。結果代表,咱們一直是在利用一個錯誤,這是一個很是廣泛的錯誤,存在於全部咱們已經測試過的C++編譯器的實現中。咱們知道,隨着C++編譯器變得更加符合標準,很快,庫將開始在更多的平臺上失敗。很不幸,由於這套機制是Boost.Python庫功能的中樞,解決問題看起來很是困難。

By early 2001 development had stabilized and few new features were being added, however a disturbing new fact came to light: Ralf had begun testing Boost.Python on pre-release versions of a compiler using the EDG front-end, and the mechanism at the core of Boost.Python responsible for handling conversions between Python and C++ types was failing to compile. As it turned out, we had been exploiting a very common bug in the implementation of all the C++ compilers we had tested. We knew that as C++ compilers rapidly became more standards-compliant, the library would begin failing on more platforms. Unfortunately, because the mechanism was so central to the functioning of the library, fixing the problem looked very difficult.

幸運的是,那一年底,Lawrence Berkeley,後來創建了Lawrence Livermore National labs,與Boost Consulting簽定了合同,來支持和發展Boost.Python,這樣就有了一個新的機會來處理庫的基本問題,從而確保了庫將來的發展。庫進行了從新設計,開始於底層的類型轉換架構,使它內置具備標準兼容性,並支持基於組件的開發(第1版中,轉換必須顯式地在模塊間導入和導出)。對Python和C++對象的關係進行了新的分析,從而能更直觀地處理C++左值和右值。

Fortunately, later that year Lawrence Berkeley and later Lawrence Livermore National labs contracted with Boost Consulting for support and development of Boost.Python, and there was a new opportunity to address fundamental issues and ensure a future for the library. A redesign effort began with the low level type conversion architecture, building in standards-compliance and support for component-based development (in contrast to version 1 where conversions had to be explicitly imported and exported across module boundaries). A new analysis of the relationship between the Python and C++ objects was done, resulting in more intuitive handling for C++ lvalues and rvalues.

關因而否維護對Python 1.5.2的兼容性,由於Python 2.2裏出現了一個強大的新的類型系統,選擇變得容易了:這個機會好的使人沒法拒絕,籍此能夠拋棄大量複雜精細的代碼,而這些代碼僅僅是用來模擬傳統的Python類。另外,Python的迭代器(iterator)和描述符(descriptor)提供了重要且優雅的工具,用來表示相似的C++構造。通用的object接口的開發進一步方便了C++程序員,免除了Python 'C' API的危險性和語法負擔。這一階段,還添加了大量其餘特性,包括C++異常翻譯,對函數重載的更好的支持,還有最重要的,用來處理指針和引用的CallPolicies。

The emergence of a powerful new type system in Python 2.2 made the choice of whether to maintain compatibility with Python 1.5.2 easy: the opportunity to throw away a great deal of elaborate code for emulating classic Python classes alone was too good to pass up. In addition, Python iterators and descriptors provided crucial and elegant tools for representing similar C++ constructs. The development of the generalized object interface allowed us to further shield C++ programmers from the dangers and syntactic burdens of the Python 'C' API. A great number of other features including C++ exception translation, improved support for overloaded functions, and most significantly, CallPolicies for handling pointers and references, were added during this period.

2002年10月,Boost.Python第2版發佈了。從那之後,開發集中於更好地支持C++運行時多態性和智能指針。特別是Peter Dimov巧妙的boost::shared_ptr 的設計,使咱們能給混和系統開發者提供一個一致的接口,用於跨越語言屏障來回移動對象而不丟失信息。剛開始,咱們擔憂Boost.Python v2實現的詭祕與複雜會阻礙貢獻者,但Pyste的出現,和其餘幾個重要特性的貢獻,證實那些擔憂是多餘的。在Python C++-sig上天天的提問,和積壓的改進請求代表了庫正在被使用。對咱們來講,將來是光明的。

In October 2002, version 2 of Boost.Python was released. Development since then has concentrated on improved support for C++ runtime polymorphism and smart pointers. Peter Dimov's ingenious boost::shared_ptr design in particular has allowed us to give the hybrid developer a consistent interface for moving objects back and forth across the language barrier without loss of information. At first, we were concerned that the sophistication and complexity of the Boost.Python v2 implementation might discourage contributors, but the emergence of Pyste and several other significant feature contributions have laid those fears to rest. Daily questions on the Python C++-sig and a backlog of desired improvements show that the library is getting used. To us, the future looks bright.

總結 Conclusions

Boost.Python在兩種功能豐富而且互補的語言環境間實現了無縫協做。由於它利用模板元編程對類型和函數進行內省,用戶沒必要去學習第三種語言:接口定義是用簡潔和可維護的C++寫的。同時,封裝系統沒必要解析C++頭文件或者描述類型系統:編譯器都給咱們作了。

Boost.Python achieves seamless interoperability between two rich and complimentary language environments. Because it leverages template metaprogramming to introspect about types and functions, the user never has to learn a third syntax: the interface definitions are written in concise and maintainable C++. Also, the wrapping system doesn't have to parse C++ headers or represent the type system: the compiler does that work for us.

計算密集型任務是C++的強項,通常不可能用純Python高效實現,然而像序列化這樣的工做,用Python很簡單,用純C++就很是困難。若是咱們能構建徹底的混合軟件系統,咱們就能以新的信心和力量來進行設計。

Computationally intensive tasks play to the strengths of C++ and are often impossible to implement efficiently in pure Python, while jobs like serialization that are trivial in Python can be very difficult in pure C++. Given the luxury of building a hybrid software system from the ground up, we can approach design with new confidence and power.

引文 Citations

[VELD1995] T. Veldhuizen, "Expression Templates, " C++ Report, Vol. 7 No. 5 June 1995, pp. 26-31. http://osl.iu.edu/~tveldhui/papers/Expression-Templates/exprtmpl.html

補充說明 Footnotes

[1] In retrospect, it seems that "thinking hybrid" from the ground up might have been better for the NLP system: the natural component boundaries defined by the pure python prototype turned out to be inappropriate for getting the desired performance and memory footprint out of the C++ core, which eventually caused some redesign overhead on the Python side when the core was moved to C++. [2] We also have some reservations about driving all C++ testing through a Python interface, unless that's the only way it will be ultimately used. Any transition across language boundaries with such different object models can inevitably mask bugs. [3] These features were expressed very differently in v1 of Boost.Python

相關文章
相關標籤/搜索