用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中import BoostPython_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 <string> #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 */ class BSTPABS_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 :public CPhone, public boost::python::wrapper<CPhone> { public: int make_call(int phone_num); std::string make_call(std::string name); CPhone& operator <<(int phone_num); CPhone& operator << (std::string name); }; #endif//BOOSTPYTHON_ABS_H
//boostpython_abs.cpp #include <boost/python.hpp> #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(int phone_num) { return this->get_override("make_call1")(); } std::string CPhoneWrap::make_call(std::string name) { return this->get_override("make_call2")(); } CPhone& CPhoneWrap::operator << (int phone_num) { return boost::python::call<CPhoneWrap&>(this->get_override("o1").ptr()); } CPhone& CPhoneWrap::operator <<(std::string name) { return boost::python::call<CPhoneWrap&>(this->get_override("o2").ptr()); } /* * Boost.Python Module Export Code bellow */ BOOST_PYTHON_MODULE(BoostPython_Module_Abs) { using namespace boost::python; class_<CPhoneWrap, boost::noncopyable>("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_<CPhoneWrap::Mode>("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 */ class BSTPCOM_API CPerson { private: int phone_num; std::string name; CPhone::Mode mode; public: CPerson() {/*initializtion here*/ } CPerson(int num, CPhone::Mode aMode = CPhone::CANCONNECTED) { this->mode = aMode; } void set(int num) { this->phone_num = num; } void set(std::string aName) { 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 <boost/python.hpp> #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>("CPerson") // // .def(init<int, optional<CPhoneWrap::Mode> >()) // // .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 <python.h> //included in dir %PYTHON_PATH%include #include <vector> #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) */ class BSTPINFO_API CInfo { public: virtual std::string get_info() { return "None Info"; } }; class BSTPINFO_API CMessageInfo :public CInfo { public: virtual std::string get_info() { return "Message Info"; } }; class BSTPINFO_API CContact { std::vector<std::string> m_vec; public: std::vector<std::string> get_contact_person() { return m_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 <boost/python.hpp> #include "boostpython_info.h" class CInfoWrap :public CInfo, public boost::python::wrapper<CInfo> { public: std::string get_info() { if (boost::python::override g = this->get_override("get_info")) { return get_info(); } return CInfo::get_info(); } }; class CMessageInfoWrap :public CMessageInfo, public boost::python::wrapper<CMessageInfo> { std::string get_info() { if (boost::python::override g = this->get_override("get_info")) { return get_info(); } return CMessageInfo::get_info(); } }; //define export methods of string-vector into python typedef std::vector<std::string> CStringVector; void push_back(CStringVector& vec, std::string str) { vec.push_back(str); } std::string pop_back(CStringVector& vec) { std::string ret = vec.back(); vec.pop_back(); return ret; } /* * Boost.Python Module code */ // //BOOST_PYTHON_MODULE(BoostPython_Module_Info) // //{ // // using namespace boost::python; // // //export vector in c++ as list in python // // class_<CStringVector>("CStringVector") // // .def("push_back", push_back) // // .def("pop_back", pop_back) // // .def("__iter__", boost::python::iterator<CStringVector>()) // // ; // // class_<CInfoWrap, boost::noncopyable>("CInfo") // // .def("get_info", &CInfo::get_info) // // ; // // class_<CMessageInfoWrap, bases<CInfo>, boost::noncopyable>("CMessageInfo") // // .def("get_info", &CMessageInfo::get_info) // // ; // // class_<CContact>("CContact") // // .def("get_contact_person", &CContact::get_contact_person) // // .def("get_contact_style", &CContact::get_contact_style, return_value_policy<manage_new_object>()) // // .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)
三. 参考文献
1. http://sourceforge.net/projects/boost/files/boost/1.50.0/boost_1_49_0.zip/download压缩包自带文档
2. python-2.7.2自带文档
转自:http://blog.csdn.net/scuhank/article/details/7769342