用 Boost.Python 写扩展库(2 类和结构体)
来源:互联网 发布:java算法基础 编辑:程序博客网 时间:2024/06/15 09:38
用 Boost.Python 写扩展库(2 类和结构体)
在 C++ 中,类和结构体本质上是一样的,唯一的区别是,类的成员默认都是 private 的,而结构体的成员默认都是 public 的。因此这里只讲类的导出方法即可。
2.1 包装简单类
当我需要导出 C++ 类给 Python 时,比如我需要导出的类的声明如下
1 class Complex2 {3 public:4 double real;5 double imag;6 Complex(double rp, double ip);7 double GetArg() const;8 };
我可以使用如下胶水代码来包装
1 class_<Complex>("Complex", init<double, double>())2 .def_readwrite("real", &Complex::real)3 .def_readwrite("imag", &Complex::imag)4 .def("GetArg", &Complex::GetArg)5 ;
这段胶水代码的意思是,先构造一个临时对象,该对象的类型是 init<double, double> (模板类 init 的一个实例),然后用字符串 "Complex" 和 这个临时对象构造另一个临时对象,该对象的类型是 class_<Complex> (模板类 class_ 的一个实例)。然后调用第二个临时对象的 def_readwrite 方法,该方法返回这个对象本身,因此可以接着再调用这个对象的 def_readwrite 方法和 def 方法。
下面是一个完整的例子,这个例子中,为了更容易写注释,我没有使用临时对象,而是给对象取了个名字叫 pyComplex,这样更方便一些:
1 /* 2 filename: Complex.cpp 3 */ 4 #include <cmath> 5 #include <boost/python.hpp>// 包含 Boost.Python 的头文件 6 7 class Complex // 复数类 8 { 9 public:10 double real; // 表示实部的成员11 double imag; // 表示虚部的成员1213 // 构造函数,以及初始化列表14 Complex(double rp, double ip):15 real(rp), // 初始化实部16 imag(ip) // 初始化虚部17 {18 }1920 // 获取复数的幅角21 double GetArg() const22 {23 return atan2(imag, real);24 }2526 };2728 using namespace boost::python; // 引入名字空间2930 BOOST_PYTHON_MODULE(ADT) // 胶水代码入口,导出一个名为“ADT”的模块31 {32 // 构造一个类型为 "boost::python::class_<Complex>" 的对象 pyComplex33 // 构造参数为字符串 "Complex"34 // 表示要将 C++ 类 Complex 导出到 Python 中去,名字也叫 "Complex"35 class_<Complex> pyComplex("Complex", no_init);3637 // 导出它的构造方法,声明它的构造方法有两个 double 类型的参数38 pyComplex.def(init<double, double>());3940 // 导出它的公有成员 real,41 // 该成员在 Complex 类中的位置是 &Complex::real42 // 导出到 Python 中之后的名字也是 "real"43 pyComplex.def_readwrite("real", &Complex::real);4445 // 导出它的公有成员 imag,46 // 该成员在 Complex 类中的位置是 &Complex::imag47 // 导出到 Python 中之后的名字也是 "imag"48 pyComplex.def_readwrite("imag", &Complex::imag);4950 // 导出它的成员方法 GetArg51 // 该方法在 Complex 类中的入口是 &Complex::GetArg52 // 导出到 Python 中之后的名字也是 "GetArg"53 pyComplex.def("GetArg", &Complex::GetArg);54 }
用上一章讲的步骤编译这个程序,生成动态连接库 ADT.so (Linux 下) 或 ADT.dll (Windows 下)。然后我可以执行一段 Python 脚本来验证一下:
>>> import ADT>>> z = ADT.Complex(-1, 1.5)>>> print z.real, '+', str(z.imag) + 'i'-1.0 + 1.5i>>> z.imag = 0>>> print z.real, '+', str(z.imag) + 'i'-1.0 + 0.0i>>> print z.GetArg()3.14159265359
这样,我就导出了一个 C++ 的复数类给 Python,同时导出了该复数类的构造方法、成员方法、公有成员变量。
我还可以包装一个公有成员变量,使它在 Python 中只能读不能写。这只需要我改用
boost::python::class_<Complex>:def_readonly() // 用于包装只读的公有成员变量
即可。
下面我们看看如何导出更多的内容。
2.2 特殊方法和运算符重载
Python 的对象有一堆特殊方法,相比于一般方法,这些方法有其特别的含义。比如在 Python 语言中语句 abs(obj) 将返回调用 obj.__abs__() 方法的结果。语句 len(obj) 将返回调用 obj.__len__() 方法的结果。语句 print obj 将在屏幕上打印 obj.__repr__() 的返回值。因此,当我导出 C++ 类的成员方法时,若导出的方法名为“__XXX__”时,我需要特别注意,这些方法在 Python 中有特别的含义,因为它们将被 Python 中特定内置函数调用的。这里“__XXX__”可能的形式和会调用它们的内置函数分别是
例如,我可以给 Complex 类增加一些这样的特殊方法:
1 class Complex 2 { 3 public: 4 double real; 5 double imag; 6 Complex(double rp, double ip); 7 double GetArg() const; 8 910 std::string ToString() const; // 新增的转换成字符串的方法11 operator double() const; // 新增的类型转换操作符12 double abs() const; // 新增的方法13 };
我可以使用如下代码来包装
1 class_<Complex>("Complex", init<double, double>())2 .def_readwrite("real", &Complex::real)3 .def_readwrite("imag", &Complex::imag)4 .def("GetArg", &Complex::GetArg)5 .def("__repr__", &Complex::ToString) // 转换成可读字符串6 .def("__float__", &Complex::operator double) // 转换到为 float7 .def("__abs__", &Complex::abs) // 求绝对值8 ;
为了方便起见,同时为了强调这几个方法的特殊性,Boost.Python 还提供了更简洁的写法用来包装特殊方法。我可以改写上面的胶水代码为
1 class_<Complex>("Complex", init<double, double>())2 .def_readwrite("real", &Complex::real)3 .def_readwrite("imag", &Complex::imag)4 .def("GetArg", &Complex::GetArg)5 .def("__repr__", &Complex::ToString) // 转换成可读字符串6 .def(float_(self)) // 转换到为 float7 .def(abs(self)) // 求绝对值8 ;
待会儿讲完操作符重载后,我会给出一个完整的例子。
其实,上面的“__XXX__”还可能有更多的形式,并且拥有更特殊的含义,即表示运算符重载。 C++ 和 Python 都支持运算符重载,不过 Python 中的运算符重载采用了更简洁的写法。比如,在 C++ 中,我要重载 Complex 类的加法运算符,我需要添加方法
Complex & Complex::operator + (Complex &L);
或者添加函数
Complex & operator + (Complex &R, Complex &L);
而在 Python 中,我只需要添加方法
class Complex: def __add__(self, L): # ...
如果我希望包装的 C++ 类支持运算符重载的话,我可以将相应的方法包装成 Python 中对应的方法。如果我给 Complex 类添加了加法运算:
class Complex{ ... Complex &operator + (const Complex &L) const; ...};
我可以用如下胶水代码来包装:
class_<Complex>("Complex", init<double, double>()).def("__add__", &Complex::operator +);
对于这些特殊的运算符重载方法,我还可以有更方便的写法来代替上面的胶水代码:
class_<Complex>("Complex", init<double, double>()).def(self + self);
有了上面介绍的工具,现在我可以给我的 Complex 类添加一坨方法重载各种常用运算符,让它用起来更方便。完整的例子如下:
1 /* 2 filename: MoreComplex.cpp 3 */ 4 #include <cmath> 5 #include <cstdio> 6 #include <string> 7 #include <boost/python.hpp>// 包含 Boost.Python 的头文件 8 9 // 复数类 10 class Complex 11 { 12 public: 13 double real; // 实部 14 double imag; // 虚部 15 16 Complex(double rp, double ip); // 构造方法 17 Complex(const Complex &c); // 拷贝构造方法 18 19 double GetArg() const; // 获取复数的幅角 20 21 std::string ToString() const; // 转换成可读字符串 22 operator double() const; // 到 double 类型的隐式转换方法 23 double abs() const; // 求模 24 25 // 复数和复数以及复数和浮点数的加法、减法和乘法 26 Complex operator + (const Complex &L) const; 27 Complex operator - (const Complex &L) const; 28 Complex operator + (double L) const; 29 Complex operator - (double L) const; 30 Complex operator * (const Complex &L) const; 31 Complex operator * (double L) const; 32 }; 33 34 using namespace boost::python; // 引入名字空间 35 36 BOOST_PYTHON_MODULE(ADT) // 胶水代码入口,导出一个名为“ADT”的模块 37 { 38 // 包装 Complex 类 39 class_<Complex>("Complex", init<double, double>()) 40 41 // 包装另外一个构造函数 42 .def(init<const Complex &>()) 43 44 // 包装公有成员 45 .def_readwrite("real", &Complex::real) 46 .def_readwrite("imag", &Complex::imag) 47 48 // 包装成员方法 49 .def("GetArg", &Complex::GetArg) 50 51 // 包装特殊方法 52 .def("\_\_repr\_\_", &Complex::ToString) 53 54 // 包装运算符重载 55 .def(float_(self)) 56 .def(abs(self)) 57 .def(self + self) 58 .def(self + other<double>()) 59 .def(self - self) 60 .def(self - other<double>()) 61 .def(self * self) 62 .def(self * other<double>()) 63 ; 64 } 65 66 // 下面是 Complex 类的各个成员方法的实现 67 68 Complex::Complex(double rp, double ip): 69 real(rp), imag(ip) 70 { 71 } 72 73 Complex::Complex(const Complex &c): 74 real(c.real), imag(c.imag) 75 { 76 } 77 78 double Complex::GetArg() const 79 { 80 return atan2(imag, real); 81 } 82 83 std::string Complex::ToString() const 84 { 85 char buf[100]; 86 if (imag == 0) 87 sprintf(buf, "%f", real); 88 else 89 sprintf(buf, "%f + %fi", real, imag); 90 91 return std::string(buf); 92 } 93 94 Complex::operator double() const 95 { 96 return abs(); 97 } 98 99 double Complex::abs() const100 {101 return sqrt(real*real + imag*imag);102 }103104 Complex Complex::operator + (const Complex &L) const105 {106 return Complex(this->real + L.real, this->imag + L.imag);107 }108109 Complex Complex::operator - (const Complex &L) const110 {111 return Complex(this->real - L.real, this->imag - L.imag);112 }113114 Complex Complex::operator + (double L) const115 {116 return Complex(this->real + L, this->imag);117 }118119 Complex Complex::operator - (double L) const120 {121 return Complex(this->real - L, this->imag);122 }123124 Complex Complex::operator * (const Complex &L) const125 {126 return Complex(real*L.real - imag*L.imag, real*L.imag + imag*L.real);127 }128129 Complex Complex::operator * (double L) const130 {131 return Complex(this->real * L, this->imag * L);132 }
用上一章讲的步骤编译这个程序,生成动态连接库 ADT.so (Linux 下) 或 ADT.dll (Windows 下)。然后我们写一个 Python 语言脚本来看一下我的 Complex 类工作得怎么样:
1 #!/usr/bin/env python 2 # filename: test.py 3 import ADT 4 a = ADT.Complex(3, 4) 5 b = ADT.Complex(6, 9) 6 c = ADT.Complex(-1, 0) 7 d = ADT.Complex(c) 8 print "a =", a 9 print "b =", b10 print "c =", c11 print "d =", d12 print "a + b =", a + b13 print "a + 1 =", a + 114 print "a * b =", a * b15 print "a * 2 =", a * 216 print "b - a =", b - a17 print "b - 3 =", b - 318 print "|a + b| =", abs(a + b)19 print "float(a) =", float(a)20 print "Arg(d) =", d.GetArg()
然后我们运行这个脚本,正确情况下我们可以看到输出
a = 3.000000 + 4.000000ib = 6.000000 + 9.000000ic = -1.000000d = -1.000000a + b = 9.000000 + 13.000000ia + 1 = 4.000000 + 4.000000ia * b = -18.000000 + 51.000000ia * 2 = 6.000000 + 8.000000ib - a = 3.000000 + 5.000000ib - 3 = 3.000000 + 9.000000i|a + b| = 15float(a) = 5.0Arg(d) = 3.14159265359
搞定!
2.3 继承
假如我写了两个 C++ 类,
class Base{public: void foo();};class Derived{public: void bar();};
如果我们错误地用如下的胶水代码包装这两个类,
class_<Base>("Base").def("foo", &Base::foo);class_<Derived>("Derived").def("bar", &Derived::bar);
那么在 Python 中我们调用 Derived 的 foo 方法会导致错误。因为, Python 并不知道 Derived 是 Base 的子类,从那里继承了一个 foo 方法。我们需要这样写:
class_<Derived, base<Base> >("Derived").def("bar", &Derived::bar);
即需要使用类模板 base<Base> 作为 class_ 的第二个模板参数即可。
2.4 总结
包装一个类,我们写的胶水代码中主要用到了如下几个类和类模板:
boost::python::class_<>和 boost::python::init<>
class_<>用来包装类;
class_<>::def() 方法用来包装类的方法;
class_<>::def_readwrite() 方法用来包装类的成员;
class_<>::def_readonly() 方法用来包装类的成员,并使它在 Python 中只读;
init<X, Y, Z> 表示类的一个构造方法接受的三个参数,分别是 X,Y,Z 类型;
boost::python::self 和 boost::python::other<>
self 表示重载的运算符中,操作数之一是对象本身
other<T> 表示重载的运算符中,操作数之一是 T 类型的对象
boost::python::base<>, 举例:class_<X, base<Y> > 表示我们要包装的类 X 继承了 类Y。
- 用 Boost.Python 写扩展库(2 类和结构体)
- 用 Boost.Python 写扩展库(2 类和结构体)
- 用Boost.Python 写扩展库
- 用 Boost.Python 写扩展库(1 简介)
- 用boost.python为python写c/c++扩展曲折配置最终成功历程
- 《扩展和嵌入python解释器》1.11 用C++写Python扩展(Writing Extensions in C++)
- 利用boost.python 扩展python
- 昨天和今天工作了2天,利用以前写好的宏,把boost::tuple 作了一个扩展。
- 用C++为python写扩展库
- 利用C++ Boost编写扩展Python模块
- 利用C++ Boost编写扩展Python模块
- 利用C++ Boost编写扩展Python模块
- boost::any的使用和扩展
- 利用boost.python 通过c++语言来扩展python (python.boost)
- 写几个函数和结构体吧~
- Boost.python 编译和使用
- python 安装dlib和boost
- 编译boost.python库
- TabHost的用法
- 从gridview选中拖拽的多行数据并放在一个datatable中执行DataTable中的查询返回新的DataTable
- VBScript: Date and Time Constants
- SQL Server中"登陆名"-"用户映射"-"数据库角色成员身份"下面10个可选项的意思
- Would MediaGo using default-capability.xml?
- 用 Boost.Python 写扩展库(2 类和结构体)
- crontab命令详解
- 在Android4.0 SDK的tools目录中运行模拟器emulator的环境变量的设置
- Linux下更改用户的主目录
- httpHandlers和httpModules深度剖析
- 自定义验证
- mysql 函数与CPU 利用率
- 学期总结
- Android 实现 Launcher