如何利用Boost.Python实现Python C/C++混合编程详解
前言
学习中如果碰到问题,参考官网例子:
D:\boost_1_61_0\libs\python\test
参考:Boost.Python中英文文档。
利用Boost.Python实现PythonC/C++混合编程
关于python与C++混合编程,事实上有两个部分
- extending所谓python程序中调用c/c++代码,其实是先处理c++代码,预先生成的动态链接库,如example.so,而在python代码中importexample;即可使用c/c++的函数.
- embeddingc++代码中调用python代码.
两者都可以用pythonc转换api,解决,具体可以去python官方文档查阅,但是都比较繁琐.
对于1,extending,常用的方案是boost.python以及swig.
swig是一种胶水语言,粘合C++,PYTHON,我前面的图形显示二叉树的文章中提到的就是利用pyqt作界面,调用c++代码使用swig生成的.so动态库.
而boost.python则直接转换,可以利用py++自动生成需要的wrapper.关于这方面的内容的入门除了boost.python官网,中文的入门资料推荐
下面话不多说了,来一起看看详细的介绍吧
导出函数
#include#include usingnamespacestd; usingnamespaceboost::python; charconst*greet() { return"hello,world"; } BOOST_PYTHON_MODULE(hello_ext) { def("greet",greet); }
python:
importhello_ext printhello_ext.greet()
导出类:
导出默认构造的函数的类
c++
#include#include usingnamespacestd; usingnamespaceboost::python; structWorld { voidset(stringmsg){this->msg=msg;} stringgreet(){returnmsg;} stringmsg; }; BOOST_PYTHON_MODULE(hello)//导出的module名字 { class_ ("World") .def("greet",&World::greet) .def("set",&World::set); }
python:
importhello planet=hello.World()#调用默认构造函数,产生类对象 planet.set("howdy")#调用对象的方法 printplanet.greet()#调用对象的方法
构造函数的导出:
#include#include usingnamespacestd; usingnamespaceboost::python; structWorld { World(stringmsg):msg(msg){}//增加构造函数 World(doublea,doubleb):a(a),b(b){}//另外一个构造函数 voidset(stringmsg){this->msg=msg;} stringgreet(){returnmsg;} doublesum_s(){returna+b;} stringmsg; doublea; doubleb; }; BOOST_PYTHON_MODULE(hello)//导出的module名字 { class_ ("World",init ()) .def(init ())//exposeanotherconstruct .def("greet",&World::greet) .def("set",&World::set) .def("sum_s",&World::sum_s); }
python测试调用:
importhello planet=hello.World(5,6) planet2=hello.World("holloworld") printplanet.sum_s() printplanet2.greet()
如果不想导出任何构造函数,则使用no_init:
class_("Abstract",no_init)
类的数据成员
#include#include usingnamespacestd; usingnamespaceboost::python; structVar { Var(stringname):name(name),value(){} stringconstname; floatvalue; }; BOOST_PYTHON_MODULE(hello_var) { class_("Var",init ()) .def_readonly("name",&Var::name)//只读 .def_readwrite("value",&Var::value);//读写 }
python调用:
importhello_var var=hello_var.Var("hello_var") var.value=3.14 #var.name='hello'#error printvar.name
C++类对象导出为Python的类对象,注意var.name不能赋值。
类的属性
//类的属性 #include#include usingnamespacestd; usingnamespaceboost::python; structNum { Num(){} floatget()const{returnval;} voidset(floatval){this->val=val;} floatval; }; BOOST_PYTHON_MODULE(hello_num) { class_ ("Num") .add_property("rovalue",&Num::get)//对外:只读 .add_property("value",&Num::get,&Num::set);//对外读写.value值会改变.rovalue值,存储着同样的数据。 }
python:
importhello_num num=hello_num.Num() num.value=10 printnum.rovalue#result:10
继承
//类的继承 #include#include #include usingnamespacestd; usingnamespaceboost::python; structBase{ virtual~Base(){}; virtualstringgetName(){return"Base";} stringstr; }; structDerived:Base{ stringgetName(){return"Derived";} }; voidb(Base*base){cout< getName()< getName()< Baseconstvolatile*get_pointer ( classBaseconstvolatile*c) { returnc; } } BOOST_PYTHON_MODULE(hello_derived) { class_ ("Base") .def("getName",&Base::getName) .def_readwrite("str",&Base::str); class_ >("Derived") .def("getName",&Derived::getName) .def_readwrite("str",&Derived::str); def("b",b); def("d",d); def("factory",factory, return_value_policy ());// }
python:
importhello_derived derive=hello_derived.factory() hello_derived.d(derive)
类的虚函数:
/* 类的虚函数,实现的功能是:可以编写Python类,来继承C++类 */ #include#include #include #include usingnamespaceboost::python; usingnamespacestd; structBase { virtual~Base(){} virtualintf(){return0;}; }; structBaseWrap:Base,wrapper { intf() { if(overridef=this->get_override("f")) returnf();//如果函数进行重载了,则返回重载的 returnBase::f();//否则返回基类 } intdefault_f(){returnthis->Base::f();} }; BOOST_PYTHON_MODULE(hello_virtual) { class_ ("Base") .def("f",&Base::f,&BaseWrap::default_f); }
python:
importhello_virtual base=hello_virtual.Base() #定义派生类,继承C++类 classDerived(hello_virtual.Base): deff(self): return42 derived=Derived() printbase.f() printderived.f()
类的运算符/特殊函数
//类的运算符/特殊函数 #include#include //#include 如果仅包含该头文件,会出错 #include #include #include #include #include usingnamespacestd; usingnamespaceboost::python; classFilePos { public: FilePos():len(0){} operatordouble()const{returnlen;};//重载类型转换符 intlen; }; //operator方法 FilePosoperator+(FilePospos,inta) { pos.len=pos.len+a; returnpos;//返回的是副本 } FilePosoperator+(inta,FilePospos) { pos.len=pos.len+a; returnpos;//返回的是副本 } intoperator-(FilePospos1,FilePospos2) { return(pos1.len-pos2.len); } FilePosoperator-(FilePospos,inta) { pos.len=pos.len-a; returnpos; } FilePos&operator+=(FilePos&pos,inta) { pos.len=pos.len+a; returnpos; } FilePos&operator-=(FilePos&pos,inta) { pos.len=pos.len-a; returnpos; } booloperator<(FilePospos1,FilePospos2) { if(pos1.len ("FilePos") .def_readwrite("len",&FilePos::len) .def(self+int()) .def(int()+self) .def(self-self) .def(self-int()) .def(self+=int()) .def(self-=other ()) .def(self ()))//__pow__ .def(abs(self))//__abs__ .def(str(self));//__str__forostream }
注意上面的:.def(pow(self,other
python:
importhello_operator filepos1=hello_operator.FilePos() filepos1.len=10 filepos2=hello_operator.FilePos() filepos2.len=20; printfilepos1-filepos2
函数
函数的调用策略。
//函数的调用策略 #include#include #include usingnamespacestd; usingnamespaceboost::python; structX { stringstr; }; structZ { intvalue; }; structY { Xx; Z*z; intz_value(){returnz->value;} }; X&f(Y&y,Z*z) { y.z=z; returny.x;//因为x是y的数据成员,x的声明周期与y进行了绑定。因为我们的目的是:Python接口应尽可能的反映C++接口 } BOOST_PYTHON_MODULE(hello_call_policy) { class_ ("Y") .def_readwrite("x",&Y::x) .def_readwrite("z",&Y::z) .def("z_value",&Y::z_value); class_ ("X") .def_readwrite("str",&X::str); class_ ("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#include #include usingnamespacestd; usingnamespaceboost::python; structX { boolf(inta) { returntrue; } boolf(inta,doubleb) { returntrue; } boolf(inta,doubleb,charc) { returntrue; } intf(inta,intb,intc) { returna+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") .def("f",fx1) .def("f",fx2) .def("f",fx3) .def("f",fx4); }
python:
importhello_overloaded x=hello_overloaded.X()#createanewobject printx.f(1)#defaultinttype printx.f(2,double(3)) printx.f(4,double(5),chr(6))#chr(6)convert*tochar printx.f(7,8,9)
默认参数
普通函数的默认参数:
然而通过上面的方式对重载函数进行封装时,就丢失了默认参数的信息。当然我们可以通过一般形式的封装,如下:
intf(int,double=3.14,charconst*="hello"); intf1(intx){returnf(x);} intf2(intx,doubley){returnf(x,y)} //intmoduleinit def("f",f);//所有参数 def("f",f2);//两个参数 def("f",f1);//一个参数
但是通过上面的形式封装很麻烦。我们可以通过宏的形式,为我们批量完成上面的功能。
C++:
//BOOST_PYTHON_FUNCTION_OVERLOADS #include#include #include usingnamespacestd; usingnamespaceboost::python; voidfoo(inta,charb=1,unsignedc=2,doubled=3) { return; } BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads,foo,1,4);//参数个数的最小为1,最大为4 BOOST_PYTHON_MODULE(hello_overloaded) { def("foo",foo,foo_overloads());//实现导出带有默认参数的函数 } python: importhello_overloaded hello_overloaded.foo(1) hello_overloaded.foo(1,chr(2)) hello_overloaded.foo(1,chr(2),3)#3对应的C++为unsignedint hello_overloaded.foo(1,chr(2),3,double(4))
成员函数的默认参数:
//使用BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS宏,完成成员函数默认参数的接口 #include#include #include usingnamespacestd; usingnamespaceboost::python; structgeorge { voidwack_em(inta,intb=0,charc='x') { return; } }; BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(george_overloads,wack_em,1,3);//参数个数的最小为1,最大为3 BOOST_PYTHON_MODULE(hello_member_overloaded) { class_ ("george") .def("wack_em",&george::wack_em,george_overloads()); }
python:
importhello_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实现构造函数的重载。
使用方法如下:
//initoptional #include#include #include usingnamespacestd; usingnamespaceboost::python; structX { X(inta,charb='D',stringc="constructor",doubleb=0.0){} }; BOOST_PYTHON_MODULE(hello_construct_overloaded) { class_ ("X") .def(init >());//init和optional }
对象接口
Python是动态类型的语言,C++是静态类型的。Python变量可能是:integer,float,list,dict,tuple,str,long,等等,还有其他类型。从Boost.Python和C++的观点来看,Python中的变量是类object的实例,在本节,我们看一下如何处理Python对象。
基本接口
//initoptional #include#include #include #include usingnamespacestd; usingnamespaceboost::python; namespacebp=boost::python; voidf(objectx) { inty=extract (x);//retrieveanintfromx } intg(objectx) { extract get_int(x); if(get_int.check()) returnget_int(); else return0; } inttest(object&x) { dictd=extract (x.attr("__dict__")); d["whatever"]=4; return0; } inttest2(dict&d) { d["helloworld"]=3; return0; } classA{ public: listlst; voidlistOperation(list&lst){}; }; //传入np.array数组对象,让C++进行处理 intadd_arr_1(object&data_obj,objectrows_obj,objectcols_obj) { PyArrayObject*data_arr=reinterpret_cast (data_obj.ptr()); float*data=static_cast (PyArray_DATA(data_arr)); //usingdata introws=extract (rows_obj); intcols=extract (cols_obj); for(inti=0;i python调用:
importhello_object dic1={"whatever":1} hello_object.test2(dic1) arr=np.array([1,2,3],dtype=float32) printarr.dtype printarr hello_object.add_arr_1(arr,1,3) printarr总结:
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。