用Boost.Python将C++代码封装为Python模块
一. 基础篇
借助Boost.Python库可以将C/C++代码方便、快捷地移植到python模块当中,实现对python模块的扩充。首先,将C++下的代码编译为动态库,并将生成的动态库命名为封装模块的名字,如:用BOOST_PYTHON_MODULE(Module_Name)宏对需要导出的函数、全局变量、类等导入Python的Module_Name模块,此时生成的动态库需要更名为Module_Name.pyd。然后,将Module_Name.pyd放在python的系统搜索目录中(通常是%PYTHON_PATH%\DLLs目录)。最后,在IDLE GUI界面或是python脚本中执行import Module_Name,这样就可以在python复用C++中定义的函数、类等而不必重写。
xuyuan77标注: 如果出现dll无法加载的情况,可以用depends工具查看是否有依赖的dll缺失,比如我在ipython中importBoostPython_Module_Abs 时无法load dll,是因为 DLLS目录下没有依赖的boost_python3-vc140-mt-x64-1_66.dll文件。
我测试的时候用的是boost 1_66版本,python3.5版本。原作者的代码有很多错误,我注释了代码,只试了import BoostPython_Module_Abs 模块。可以成功运行。
二. 实例篇
下面的实例代码主要针对抽象类、带默认实现虚函数的类、类的成员函数及操作符重载、带默认参数的函数(包括构造函数)、派生类、纯虚函数、返回对象及字符串的函数等的封装方法,基本概括了C+扩展到python模块的常见类型。
//boostpython_abs.h
#include #ifndef BOOSTPYTHON_ABS_H#define BOOSTPYTHON_ABS_H
/**brief:
* wrap c/c++ code as dll and export the class/function interfaces
* to python as modules with boost-library
*author:
* hank
*history:
* created 2012-07-13*/#ifndef BSTPABS_API#define BSTPABS_API __declspec(dllimport)
#else
#define BSTPABS_API __declspec(dllexport)
#endif
/** 1.export the abstract class into python module
* 2.abstract class with member over-load functions|operators*/
classBSTPABS_API CPhone
{public:enum Mode { CANCONNECTED = 0, CONNECTED, PAUSE, DISCONNECTED };public:
CPhone(std::string owner = "") {}virtual int make_call(int phone_num) = 0;virtual std::string make_call(std::string name) = 0;virtual CPhone& operator << (int phone_num) = 0;virtual CPhone& operator << (std::string name) = 0;
};//here,wrap the CPhone class
class CPhoneWrap :publicCPhone,public boost::python::wrapper{public:int make_call(intphone_num);
std::string make_call(std::stringname);
CPhone& operator <<(intphone_num);
CPhone& operator << (std::stringname);
};#endif//BOOSTPYTHON_ABS_H
//boostpython_abs.cpp
#include#include"boostpython_abs.h"
//define function pointers of overload functions
int (CPhone::*make_call1)(int) = &CPhone::make_call;
std::string(CPhone::*make_call2)(std::string) = &CPhone::make_call;
CPhone& (CPhone::*o1)(int) = &CPhone::operator<<;
CPhone& (CPhone::*o2)(std::string) = &CPhone::operator<<;int CPhoneWrap::make_call(intphone_num)
{return this->get_override("make_call1")();
}
std::string CPhoneWrap::make_call(std::stringname)
{return this->get_override("make_call2")();
}
CPhone& CPhoneWrap::operator << (intphone_num)
{return boost::python::call(this->get_override("o1").ptr());
}
CPhone& CPhoneWrap::operator <<(std::stringname)
{return boost::python::call(this->get_override("o2").ptr());
}/** Boost.Python Module Export Code bellow*/BOOST_PYTHON_MODULE(BoostPython_Module_Abs)
{using namespaceboost::python;
class_("CPhone")
.def("<<", pure_virtual(o1), return_internal_reference<>())
.def("<<", pure_virtual(o2), return_internal_reference<>())
.def("make_call", pure_virtual(make_call1))
.def("make_call", pure_virtual(make_call2))
;
enum_("Mode")
.value("CANCONNECTED", CPhoneWrap::Mode::CANCONNECTED)
.value("CONNECTED", CPhoneWrap::Mode::CONNECTED)
.value("PAUSE", CPhoneWrap::Mode::PAUSE)
.value("DISCONNECTED", CPhoneWrap::Mode::DISCONNECTED)
;
}
//boostpython_com.h
#ifndef BOOSTPYTHON_COM_H#define BOOSTPYTHON_COM_H#include"boostpython_abs.h"#ifndef BSTPCOM_API#define BSTPCOM_API __declspec(dllimport)
#else
#define BSTPCOM_API __declspec(dllexport)
#endif
/** 1.common class with member over-load functions|operators
* 2.with default parameter in constructor function*/
classBSTPCOM_API CPerson
{private:intphone_num;
std::stringname;
CPhone::Mode mode;public:
CPerson() {/*initializtion here*/}
CPerson(int num, CPhone::Mode aMode =CPhone::CANCONNECTED)
{this->mode =aMode;
}void set(intnum)
{this->phone_num =num;
}void set(std::stringaName)
{this->name =aName;
}void setall(int num, std::string name = "") {/*do something here*/}
CPerson&operator<<(int phone_num) { return *this; }
CPerson&operator<<(std::string name) { return *this; }
CPerson& write(int phone_num) { return *this; }
CPerson& write(std::string name) { return *this; }
};#endif//BOOSTPYTHON_COM_H
//boostpython_com.cpp
#include#include"boostpython_com.h"#include"boostpython_abs.h"
/** the function pointers of overload functions*/
//void (CPerson::*set1)(int) = &CPerson::set;//
//void (CPerson::*set2)(std::string) = &CPerson::set;//
//CPerson& (CPerson::*write1)(int) = &CPerson::write;//
//CPerson& (CPerson::*write2)(std::string) = &CPerson::write;
/** Boost.Python Module Export Code bellow*/
//BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(setall_overload, setall, 1, 2)//
//BOOST_PYTHON_MODULE (BoostPython_Module_Com)//
//{//
//using namespace boost::python;//
//class_("CPerson")//
//.def(init >())//
//.def(self << int())//
//.def(self << std::string())//
//.def("setall", &CPerson::setall, setall_overload())//
//.def("set", set1)//
//.def("set", set2)//
//.def("write", write1)//
//.def("write", write2);//
//}
//boostpython_info.h
#ifndef BOOSTPYTHON_INFO_H#define BOOSTPYTHON_INFO_H#include"boostpython_abs.h"#include"boostpython_com.h"#include //included in dir %PYTHON_PATH%\include
#include#ifndef BSTPINFO_API#define BSTPINFO_API __declspec(dllimport)
#else
#define BSTPINFO_API __declspec(dllexport)
#endif
/** 1. virtual functions with default implements
* 2. stl export
* 3. with char* return function,must wrap it
* 4. global function(c-style)*/
classBSTPINFO_API CInfo
{public:virtual std::string get_info() { return "None Info"; }
};class BSTPINFO_API CMessageInfo :publicCInfo
{public:virtual std::string get_info() { return "Message Info"; }
};classBSTPINFO_API CContact
{
std::vectorm_vec;public:
std::vectorget_contact_person()
{returnm_vec;
}
CInfo* get_contact_style() { return new(std::nothrow)CMessageInfo(); }void set_contact_style(CInfo*) {/*todo:xxx*/}char* get_info() { return "return char* in python"; }//you should wrap char* f(),however,const char* f() this not needed
PyObject* get_info_wrap() { return Py_BuildValue("s", get_info()); }
};#endif //BOOSTPYTHON_INFO_H
#include #include"boostpython_info.h"
class CInfoWrap :publicCInfo,public boost::python::wrapper{public:
std::stringget_info()
{if (boost::python::override g = this->get_override("get_info"))
{returnget_info();
}returnCInfo::get_info();
}
};class CMessageInfoWrap :publicCMessageInfo,public boost::python::wrapper{
std::stringget_info()
{if (boost::python::override g = this->get_override("get_info"))
{returnget_info();
}returnCMessageInfo::get_info();
}
};//define export methods of string-vector into python
typedef std::vectorCStringVector;void push_back(CStringVector& vec, std::stringstr)
{
vec.push_back(str);
}
std::string pop_back(CStringVector&vec)
{
std::string ret =vec.back();
vec.pop_back();returnret;
}/** Boost.Python Module code*/
//
//BOOST_PYTHON_MODULE(BoostPython_Module_Info)//
//{//
//using namespace boost::python;//
// //export vector in c++ as list in python//
//class_("CStringVector")//
//.def("push_back", push_back)//
//.def("pop_back", pop_back)//
//.def("__iter__", boost::python::iterator())//
//;//
//class_("CInfo")//
//.def("get_info", &CInfo::get_info)//
//;//
//class_, boost::noncopyable>("CMessageInfo")//
//.def("get_info", &CMessageInfo::get_info)//
//;//
//class_("CContact")//
//.def("get_contact_person", &CContact::get_contact_person)//
//.def("get_contact_style", &CContact::get_contact_style, return_value_policy())//
//.def("set_contact_style", &CContact::set_contact_style)//
//.def("get_info", &CContact::get_info_wrap);//
//}
#!/bin/python
#demo.py'''brief:
demonstratione of C/C++dll export into python modules
author:
hank/2012-07-13
'''import BoostPython_Module_Abs
import BoostPython_Module_Com
import BoostPython_Module_Info
info=BoostPython_Module_Info.CMessageInfo()
contact=BoostPython_Module_Info.CContact()
contact.set_contact_style(info)
getinfo=contact.get_contact_style()
print(type(getinfo)) #the type should be CMessageInfo instance
chars=contact.get_info()
print(chars)
三. 参考文献
2. python-2.7.2自带文档
转自:http://blog.csdn.net/scuhank/article/details/7769342