cppyy 概述

cppyy 概述#

cppyy 是一个自动的、运行时的 Python-C++绑定生成器,用于从Python调用C++以及从C++调用Python。运行时生成能够实现更详细的特化以获得更高的性能,延迟加载以减少大规模项目中的内存使用,Python端的交叉继承和回调以便与C++框架一起工作,运行时模板实例化,自动对象向下转换,异常映射,以及C++库的交互式探索。cppyy在不需要任何语言扩展、中间语言或手写样板代码的情况下提供了这些功能。

cppyy 基于 Cling,这是 C++ 解释器,用于匹配 Python 的动态性、交互性和运行时行为。

import cppyy

cppyy.cppdef("""
class MyClass {
public:
    MyClass(int i) : m_data(i) {}
    virtual ~MyClass() {}
    virtual int add_int(int i) { return m_data + i; }
    int m_data;
};""")
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 1
----> 1 import cppyy
      3 cppyy.cppdef("""
      4 class MyClass {
      5 public:
   (...)
      9     int m_data;
     10 };""")

ModuleNotFoundError: No module named 'cppyy'
from cppyy.gbl import MyClass
m = MyClass(42)
cppyy.cppdef("""
void say_hello(MyClass* m) {
    std::cout << "Hello, the number is: " << m->m_data << std::endl;
}""")
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[2], line 1
----> 1 from cppyy.gbl import MyClass
      2 m = MyClass(42)
      3 cppyy.cppdef("""
      4 void say_hello(MyClass* m) {
      5     std::cout << "Hello, the number is: " << m->m_data << std::endl;
      6 }""")

ModuleNotFoundError: No module named 'cppyy'
MyClass.say_hello = cppyy.gbl.say_hello
m.say_hello()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 MyClass.say_hello = cppyy.gbl.say_hello
      2 m.say_hello()

NameError: name 'cppyy' is not defined
m.m_data = 13
m.say_hello()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 m.m_data = 13
      2 m.say_hello()

NameError: name 'm' is not defined
class PyMyClass(MyClass):
    def add_int(self, i):  # python side override (CPython only)
        return self.m_data + 2*i
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[5], line 1
----> 1 class PyMyClass(MyClass):
      2     def add_int(self, i):  # python side override (CPython only)
      3         return self.m_data + 2*i

NameError: name 'MyClass' is not defined
cppyy.cppdef("int callback(MyClass* m, int i) { return m->add_int(i); }")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[6], line 1
----> 1 cppyy.cppdef("int callback(MyClass* m, int i) { return m->add_int(i); }")

NameError: name 'cppyy' is not defined
cppyy.gbl.callback(m, 2)             # calls C++ add_int
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[7], line 1
----> 1 cppyy.gbl.callback(m, 2)             # calls C++ add_int

NameError: name 'cppyy' is not defined
cppyy.gbl.callback(PyMyClass(1), 2)  # calls Python-side override
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[8], line 1
----> 1 cppyy.gbl.callback(PyMyClass(1), 2)  # calls Python-side override

NameError: name 'cppyy' is not defined

凭借现代的 C++ 编译器作为后盾,cppyy 是未来证明的。请看下面这个使用 boost::any 的会话,这是在 C++ 中允许异质容器的封装类型。Boost 库以其对现代 C++ 的大胆使用和大量模板使用而闻名:

import cppyy
cppyy.include('boost/any.hpp')       # assumes you have boost installed
from cppyy.gbl import std, boost
val = boost.any()                    # the capsule
>>> val.__assign__(std.vector[int]())    # assign it a std::vector<int>
<cppyy.gbl.boost.any object at 0xf6a8a0>
>>> val.type() == cppyy.typeid(std.vector[int])    # verify type
True
>>> extract = boost.any_cast[int](std.move(val))   # wrong cast
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
cppyy.gbl.boost.bad_any_cast: Could not instantiate any_cast<int>:
  int boost::any_cast(boost::any&& operand) =>
    wrapexcept<boost::bad_any_cast>: boost::bad_any_cast: failed conversion using boost::any_cast
>>> extract = boost.any_cast[std.vector[int]](val) # correct cast
>>> type(extract) is std.vector[int]
True
>>> extract += xrange(100)
>>> len(extract)
100
>>> val.__assign__(std.move(extract))    # move forced
<cppyy.gbl.boost.any object at 0xf6a8a0>
>>> len(extract)                         # now empty (or invalid)
0
>>> extract = boost.any_cast[std.vector[int]](val)
>>> list(extract)
[0, 1, 2, 3, 4, 5, 6, ..., 97, 98, 99]

当然,没有理由从 Python 使用 Boost(实际上,这个例子需要 pythonizations),但它展示了 cppyy 无缝支持许多高级 C++ 特性。

cppyy 适用于 CPython(v2和v3)以及 PyPy,在后者中达到类似 C++ 的性能。它审慎使用预编译头、动态加载和延迟实例化,以支持由数百万行代码和数千个类组成的 C++ 程序。cppyy 最小化依赖关系,以便在分布式、异构开发环境中使用。