使用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

相关文章
相关标签/搜索