XY

没有任何借口!!!
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

【转】利用Boost.Python将C++代码封装为Python模块

Posted on 2018-02-05 21:21  路缘  阅读(6335)  评论(0编辑  收藏  举报

用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