如何利用Boost.Python实现Python C/C++混合编程详解
前言
学习中如果碰到问题,参考官网例子:
d:\boost_1_61_0\libs\python\test
参考:boost.python 中英文文档。
利用boost.python实现python c/c++混合编程
关于python与c++混合编程,事实上有两个部分
- extending 所谓python 程序中调用c/c++代码, 其实是先处理c++代码, 预先生成的动态链接库, 如example.so, 而在python代码中import example;即可使用c/c++的函数 .
- embedding c++代码中调用 python 代码.
两者都可以用 python c 转换api,解决,具体可以去python官方文档查阅,但是都比较繁琐.
对于1,extending,常用的方案是boost.python以及swig.
swig是一种胶水语言,粘合c++,python,我前面的图形显示二叉树的文章中提到的就是利用pyqt作界面,调用c++代码使用swig生成的.so动态库.
而boost.python则直接转换,可以利用py++自动生成需要的wrapper.关于这方面的内容的入门除了boost.python官网,中文的入门资料推荐
下面话不多说了,来一起看看详细的介绍吧
导出函数
#include<string> #include<boost/python.hpp> using namespace std; using namespace boost::python; char const * greet() { return "hello,world"; } boost_python_module(hello_ext) { def("greet", greet); }
python:
import hello_ext print hello_ext.greet()
导出类:
导出默认构造的函数的类
c++
#include<string> #include<boost/python.hpp> using namespace std; using namespace boost::python; struct world { void set(string msg) { this->msg = msg; } string greet() { return msg; } string msg; }; boost_python_module(hello) //导出的module 名字 { class_<world>("world") .def("greet", &world::greet) .def("set", &world::set); }
python:
import hello planet = hello.world() # 调用默认构造函数,产生类对象 planet.set("howdy") # 调用对象的方法 print planet.greet() # 调用对象的方法
构造函数的导出:
#include<string> #include<boost/python.hpp> using namespace std; using namespace boost::python; struct world { world(string msg):msg(msg){} //增加构造函数 world(double a, double b):a(a),b(b) {} //另外一个构造函数 void set(string msg) { this->msg = msg; } string greet() { return msg; } double sum_s() { return a + b; } string msg; double a; double b; }; boost_python_module(hello) //导出的module 名字 { class_<world>("world",init<string>()) .def(init<double,double>()) // expose another construct .def("greet", &world::greet) .def("set", &world::set) .def("sum_s", &world::sum_s); }
python 测试调用:
import hello planet = hello.world(5,6) planet2 = hello.world("hollo world") print planet.sum_s() print planet2.greet()
如果不想导出任何构造函数,则使用no_init:
class_<abstract>("abstract",no_init)
类的数据成员
#include<string> #include<boost/python.hpp> using namespace std; using namespace boost::python; struct var { var(string name):name(name),value(){} string const name; float value; }; boost_python_module(hello_var) { class_<var>("var", init<string>()) .def_readonly("name", &var::name) //只读 .def_readwrite("value", &var::value); //读写 }
python调用:
import hello_var var = hello_var.var("hello_var") var.value = 3.14 # var.name = 'hello' # error print var.name
c++类对象导出为python的类对象,注意var.name不能赋值。
类的属性
// 类的属性 #include<string> #include<boost/python.hpp> using namespace std; using namespace boost::python; struct num { num(){} float get() const { return val; } void set(float val) { this->val = val; } float val; }; boost_python_module(hello_num) { class_<num>("num") .add_property("rovalue", &num::get) // 对外:只读 .add_property("value", &num::get, &num::set);// 对外读写 .value值会改变.rovalue值,存储着同样的数据。 }
python:
import hello_num num = hello_num.num() num.value = 10 print num.rovalue # result: 10
继承
// 类的继承 #include<string> #include<iostream> #include<boost/python.hpp> using namespace std; using namespace boost::python; struct base { virtual ~base() {}; virtual string getname() { return "base"; } string str; }; struct derived : base { string getname() { return "derived"; } }; void b(base *base) { cout << base->getname() << endl; }; void d(derived *derived) { cout << derived->getname() << endl; }; base * factory() { return new derived; } /* 下面的额外的代码如果去掉会报错。 解决地址:http://*.com/questions/38261530/unresolved-external-symbols-since-visual-studio-2015-update-3-boost-python-link/38291152#38291152 */ namespace boost { template <> base const volatile * get_pointer<class base const volatile >( class base const volatile *c) { return c; } } boost_python_module(hello_derived) { class_<base>("base") .def("getname", &base::getname) .def_readwrite("str", &base::str); class_<derived, bases<base> >("derived") .def("getname", &derived::getname) .def_readwrite("str", &derived::str); def("b", b); def("d", d); def("factory", factory, return_value_policy<manage_new_object>());// }
python:
import hello_derived derive = hello_derived.factory() hello_derived.d(derive)
类的虚函数:
/* 类的虚函数,实现的功能是:可以编写python类,来继承c++类 */ #include<boost/python.hpp> #include<boost/python/wrapper.hpp> #include<string> #include<iostream> using namespace boost::python; using namespace std; struct base { virtual ~base() {} virtual int f() { return 0; }; }; struct basewrap : base, wrapper<base> { int f() { if (override f = this->get_override("f")) return f(); //如果函数进行重载了,则返回重载的 return base::f(); //否则返回基类 } int default_f() { return this->base::f(); } }; boost_python_module(hello_virtual) { class_<basewrap, boost::noncopyable>("base") .def("f", &base::f, &basewrap::default_f); }
python:
import hello_virtual base = hello_virtual.base() # 定义派生类,继承c++类 class derived(hello_virtual.base): def f(self): return 42 derived = derived() print base.f() print derived.f()
类的运算符/特殊函数
// 类的运算符/特殊函数 #include<string> #include<iostream> // #include<boost/python.hpp> 如果仅包含该头文件,会出错 #include <boost/python/operators.hpp> #include <boost/python/class.hpp> #include <boost/python/module.hpp> #include <boost/python/def.hpp> #include <boost/operators.hpp> using namespace std; using namespace boost::python; class filepos { public: filepos() :len(0) {} operator double()const { return len; };//重载类型转换符 int len; }; // operator 方法 filepos operator+(filepos pos, int a) { pos.len = pos.len + a; return pos; //返回的是副本 } filepos operator+(int a, filepos pos) { pos.len = pos.len + a; return pos; //返回的是副本 } int operator-(filepos pos1, filepos pos2) { return (pos1.len - pos2.len); } filepos operator-(filepos pos, int a) { pos.len = pos.len - a; return pos; } filepos &operator+=(filepos & pos, int a) { pos.len = pos.len + a; return pos; } filepos &operator-=(filepos & pos, int a) { pos.len = pos.len - a; return pos; } bool operator<(filepos pos1, filepos pos2) { if (pos1.len < pos2.len) return true; return false; } //特殊的方法 filepos pow(filepos pos1, filepos pos2) { filepos res; res.len = std::pow(pos1.len, pos2.len); return res; } filepos abs(filepos pos) { filepos res; res.len = std::abs(pos.len); return res; } ostream& operator<<(ostream& out, filepos pos) { out << pos.len; return out; } boost_python_module(hello_operator) { class_<filepos>("filepos") .def_readwrite("len",&filepos::len) .def(self + int()) .def(int() + self) .def(self - self) .def(self - int()) .def(self += int()) .def(self -= other<int>()) .def(self < self) .def(float_(self))//特殊方法 , __float__ .def(pow(self, other<filepos>())) // __pow__ .def(abs(self)) // __abs__ .def(str(self)); // __str__ for ostream }
注意上面的:.def(pow(self, other<filepos>()))模板后面要加上括号。也要注意头文件的包含,否则会引发错误。
python:
import hello_operator filepos1 = hello_operator.filepos() filepos1.len = 10 filepos2 = hello_operator.filepos() filepos2.len = 20; print filepos1 - filepos2
函数
函数的调用策略。
// 函数的调用策略 #include<string> #include<iostream> #include<boost/python.hpp> using namespace std; using namespace boost::python; struct x { string str; }; struct z { int value; }; struct y { x x; z *z; int z_value() { return z->value; } }; x & f(y &y, z*z) { y.z = z; return y.x; //因为x是y的数据成员,x的声明周期与y进行了绑定。因为我们的目的是:python接口应尽可能的反映c++接口 } boost_python_module(hello_call_policy) { class_<y>("y") .def_readwrite("x", &y::x) .def_readwrite("z", &y::z) .def("z_value", &y::z_value); class_<x>("x") .def_readwrite("str", &x::str); class_<z>("z") .def_readwrite("value", &z::value); // return_internal_reference<1 表示返回的值与第一个参数有关系:即第一个参数是返回对象的拥有者(y和x都是引用的形式)。 // with_custodian_and_ward<1, 2> 表示第二个参数的生命周期依赖于第一个参数的生命周期。 def("f", f, return_internal_reference<1, with_custodian_and_ward<1, 2> >()); }
函数重载
// overloading #include<string> #include<iostream> #include<boost/python.hpp> using namespace std; using namespace boost::python; struct x { bool f(int a) { return true; } bool f(int a, double b) { return true; } bool f(int a, double b, char c) { return true; } int f(int a, int b, int c) { return a + b + c; } }; bool (x::*fx1)(int) = &x::f; bool(x::*fx2)(int, double) = &x::f; bool(x::*fx3)(int, double,char) = &x::f; int(x::*fx4)(int, int,int) = &x::f; boost_python_module(hello_overloaded) { class_<x>("x") .def("f", fx1) .def("f", fx2) .def("f", fx3) .def("f", fx4); }
python:
import hello_overloaded x = hello_overloaded.x() # create a new object print x.f(1) # default int type print x.f(2,double(3)) print x.f(4,double(5),chr(6)) # chr(6) convert * to char print x.f(7,8,9)
默认参数
普通函数的默认参数:
然而通过上面的方式对重载函数进行封装时,就丢失了默认参数的信息。当然我们可以通过一般形式的封装,如下:
int f(int,double = 3.14,char const * = "hello"); int f1(int x){ return f(x);} int f2(int x,double y){return f(x,y)} //int module init def("f",f); // 所有参数 def("f",f2); //两个参数 def("f",f1); //一个参数
但是通过上面的形式封装很麻烦。我们可以通过宏的形式,为我们批量完成上面的功能。
c++:
// boost_python_function_overloads #include<string> #include<iostream> #include<boost/python.hpp> using namespace std; using namespace boost::python; void foo(int a, char b = 1, unsigned c = 2, double d = 3) { return; } boost_python_function_overloads(foo_overloads, foo, 1, 4); // 参数个数的最小为1,最大为4 boost_python_module(hello_overloaded) { def("foo", foo, foo_overloads()); //实现导出带有默认参数的函数 } python: import hello_overloaded hello_overloaded.foo(1) hello_overloaded.foo(1,chr(2)) hello_overloaded.foo(1,chr(2),3) # 3对应的c++为unsigned int hello_overloaded.foo(1,chr(2),3,double(4))
成员函数的默认参数:
//使用boost_python_member_function_overloads 宏,完成成员函数默认参数的接口 #include<string> #include<iostream> #include<boost/python.hpp> using namespace std; using namespace boost::python; struct george { void wack_em(int a, int b = 0, char c = 'x') { return; } }; boost_python_member_function_overloads(george_overloads, wack_em, 1, 3); // 参数个数的最小为1,最大为3 boost_python_module(hello_member_overloaded) { class_<george>("george") .def("wack_em", &george::wack_em, george_overloads()); }
python:
import hello_member_overloaded c = hello_member_overloaded.george() c.wack_em(1) c.wack_em(1,2) c.wack_em(1,2,chr(3))
利用init和optional实现构造函数的重载。
使用方法如下:
// init optional #include<string> #include<iostream> #include<boost/python.hpp> using namespace std; using namespace boost::python; struct x { x(int a, char b = 'd', string c = "constructor", double b = 0.0) {} }; boost_python_module(hello_construct_overloaded) { class_<x>("x") .def(init<int, optional<char, string, double> >()); // init 和 optional }
对象接口
python 是动态类型的语言,c++是静态类型的。python变量可能是:integer,float ,list ,dict,tuple,str,long,等等,还有其他类型。从boost.python和c++的观点来看,python中的变量是类object的实例,在本节,我们看一下如何处理python对象。
基本接口
// init optional #include<string> #include<iostream> #include<boost/python.hpp> #include <numpy/arrayobject.h> using namespace std; using namespace boost::python; namespace bp = boost::python; void f(object x) { int y = extract<int>(x); // retrieve an int from x } int g(object x) { extract<int> get_int(x); if (get_int.check()) return get_int(); else return 0; } int test(object &x) { dict d = extract<dict>(x.attr("__dict__")); d["whatever"] = 4; return 0; } int test2(dict & d) { d["helloworld"] = 3; return 0; } class a { public: list lst; void listoperation(list &lst) {}; }; // 传入np.array数组对象,让c++进行处理 int add_arr_1(object & data_obj, object rows_obj, object cols_obj) { pyarrayobject* data_arr = reinterpret_cast<pyarrayobject*>(data_obj.ptr()); float * data = static_cast<float *>(pyarray_data(data_arr)); // using data int rows = extract<int>(rows_obj); int cols = extract<int>(cols_obj); for (int i = 0; i < rows*cols; i++) { data[i] += 1; } return 0; } boost_python_module(hello_object) { def("test", test); def("test2", test2); def("add_arr_1", add_arr_1); }
python 调用:
import hello_object dic1 = {"whatever":1} hello_object.test2(dic1) arr = np.array([1,2,3],dtype = float32) print arr.dtype print arr hello_object.add_arr_1(arr,1,3) print arr
总结:
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。