bosst-python使用 混合编程

1.为什么要混合编程?

python的优缺点

优点:编写简单

缺点:Python的运行速度是很慢的

注意:

如果只是IO耗时,换成C++程序意义也不大

2.常用的混合编程方法

  1. ctypes

    ctypes是python的标准库,可以直接调用c/c++编写的动态链接库

    但是C++程序员需要以C语言的调用约定来提供接口,没有类,没有重载函数,没有模板,没有C++异常

  2. Cython语言(python)

    一种类似于Python语言的一种新型语言编写预定功能的代码,然后将这些代码转换成为C语言编译成为Python语言可以直接调用的二进制模块。

  3. boost.python

    与ctypes/Cython形成鲜明的对比,boost.python倾向于让C++程序员拥有更熟悉的编程环境。

  4. SWIG或SIP(c语言数据类型 映射文件)

    通过编写一个接口文件,使用类似于C/C++语法——声明函数、类型的信息,然后使用特殊的工具为C/C++的代码生成Python的接口代码

3.boost::python环境搭建

  1. 安装和配置boost

    a.安装boost

    sudo apt-get install libboost-all-dev    
    

    b.创建libboost_python3.so的软连接

    查找so库的位置
    sudo find / -name "libboost_python-py35.so"
    进入库的路径
    cd /usr/lib/x86_64-linux-gnu/
    创建软连接
    sudo ln -s libboost_python-py35.so libboost_python3.so
    
  2. 如果出现如下问题:

fatal error: pyconfig.h: No such file or directory

解决办法:

a.找到pyconfig.h文件位置

locate pyconfig.h

b.添加路径到环境变量

编辑.bashrc添加如下内容(路径为查找到的路径)

export CPLUS_INCLUDE_PATH=/usr/include/python3.4m

也可以配置anconda中的
export CPLUS_INCLUDE_PATH=/home/cai/anaconda3/pkgs/python-3.6.2-0/include/python3.6m
这个配置的是头文件,我想c++的东西在环境变量中估计都是头文件

python版本含义:

--with-pydebug (flag: d)
--with-pymalloc (flag: m)
--with-wide-unicode (flag: u)

4.helloworld

1.代码

// 必须包含这个头文件
#include <boost/python.hpp>
// 使用boost:python的命名空间
using namespace boost::python;

//定义的函数
char const* greet()
{
    return "hello, world";
}

//宏对需要导出的函数、全局变量、类等导入Python的Module_Name模块
BOOST_PYTHON_MODULE(hello)//模块名
{
    //第一个:对应的python函数名
    //第二个:需要导出的c++函数名
    def("greet", greet);
}
  1. 生成python模块so
g++ -shared -o hello.so -fPIC -I /usr/include/python3.4m/ main.cpp -lpython3.4m -lboost_python3

参数含义:

-shared    // 指定生成动态链接库
-o    // 生成的动态链接库的名称
-fPIC    // 表示使用地址无关代码
-I(大写的i)    // 表示将/usr/include/python3.4m/目录作为第一个寻找头文件的目录(主要查找pyconfig.h,如果配置了环境变量可以不指定)
-l    // 指定需链接的库名

4.通过clion生成python模块

配置CMakeLists.txt

cmake_minimum_required(VERSION 3.14)
project(BoostPython)

set(CMAKE_CXX_STANDARD 14)

add_definitions(-fPIC)

# pyconfig.h的路径
include_directories(/usr/include/python3.4m)

# 注意库名称要和代码中指定的一致
add_library(hello SHARED main.cpp)
# 连接
target_link_libraries(
        hello
        python3.4m
        boost_python3
)

5.结构体和对象

代码

BOOST_PYTHON_MODULE(class_example){
    class_<Student>("Student")
            .def("sayHello",&Student::sayHello);
}

6.对象的构造函数

BOOST_PYTHON_MODULE(constructor){
    class_<Student>("Student")//无参
            .def(init<string>())//一个参数
            .def(init<string,int>())//两个参数
            .def("sayHello",&Student::sayHello);
}

注意:没有构造函数 可以使用no_init表示

7.成员变量

def_readonly:只读属性
def_readwrite:读写属性
add_property:将get和set方法包装成属性

代码

class Student {
private:
    double mNumber;
public:
    //不可修改
    const string  name;
    //可读可写
    int age;

    Student();

    ~Student();

    //获取mNumber
    double getNumber();

    //设置mNumber
    void setNumber(double value);

    //打印三个属性
    void printMembers();
};

可以把getNumber和setNumber包装成一个属性

包装:

BOOST_PYTHON_MODULE(members){
    class_<Student>("Student")
            .def_readonly("name",&Student::name)
            .def_readwrite("age",&Student::age)
            //必须要先get再set
            .add_property("number",&Student::getNumber,&Student::setNumber)
            .def("printMembers",&Student::printMembers);
}

8.多态

class Base
{
public:
    virtual std::string name() const { return "Base"; }
    virtual ~Base() {}
};

class Derived : public Base
{
public:
    virtual std::string name() const { return "Derived"; }
};

void fb(Base *b)
{
    std::cout << b->name() << " called." << std::endl;
}

void fd(Derived *d)
{
    std::cout << "Derived " << d->name() << " called." << std::endl;
}

包装:

BOOST_PYTHON_MODULE(inheritance)
{
    class_<Base, boost::noncopyable>("Base")//没有拷贝构造
            .def("name", &Base::name);

    //对于继承,则需指明继承对象
    class_<Derived, bases<Base> >("Derived");

    def("fb", fb);
    def("fd", fd);
    
}

9.静态成员和静态方法

指定静态方法

staticmethod

#include <iostream>
using namespace std;

class Example {
public:
    static string name;

    static void sayHello();
};

string Example::name = "张三";

void Example::sayHello(){
    cout<<"hello world"<<endl;
}

#include <boost/python.hpp>

using namespace boost::python;

boost包装:

BOOST_PYTHON_MODULE (static_example) {
    class_<Example>("Example", no_init)
            .def("sayHello", &Example::sayHello)
            .def_readwrite("name",&Example::name)
            .staticmethod("sayHello");
}

python调用:

from static_example import Example
# 调用静态方法
Example.sayHello()
# 调用静态属性
print(Example.name)

注意:

对于静态属性不用特别处理,静态方法需要特殊处理

10.方法重载

对于方法重载分为两种:

1.普通方法重载

2.默认参数函数(类成员以及普通函数)

代码:

class Student{
public:
    void sayHello(){
        cout<<"空 sayHello"<<endl;
    }
    void sayHello(int age){
        cout<<"int sayHello"<<endl;
    }
    void sayHello(string name,int age){
        cout<<"string和int sayHello"<<endl;
    }

    void haha(string name,int age=10,double score = 34){
        cout<<"调用了默认参数的函数"<<endl;
    }
};

//普通默认参数函数
void hehe(string name,int age=1,double score = 1.123){
    cout<<"调用了sayHello"<<name<<age<<score<<endl;
}

处理结果:

//处理默认参数函数的重载问题
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(haha_overloads, haha, 1, 3)
//处理普通方法重载
BOOST_PYTHON_FUNCTION_OVERLOADS(sayHello_overloads, hehe, 1, 3)
    
BOOST_PYTHON_MODULE(overload)
{
    //普通重载的方法
    void        (Example::*d1)()             = &Example::doit;
    std::string (Example::*d2)(unsigned int) = &Example::doit;
    void        (Example::*d3)(std::string)  = &Example::doit;
    
    class_<Example>("Example")
            .def("__str__", &Example::print)
            .def("doit", d1)
            .def("doit", d2)
            .def("doit", d3)
            //默认参数的方法重载
            .def("makeIt", &Example::makeIt, makeIt_overloads());
}

11.枚举

使用enum_包装枚举

格式:

enum_<name>("name")
            .value("value1", value1)
            .value("value2", value2)
            .value("value3", value3);

boost封装:

#include <boost/python.hpp>

using namespace boost::python;

enum color { red = 1, green = 2, blue = 4 };

BOOST_PYTHON_MODULE(enum_example)
{
    enum_<color>("color")
            .value("red", red)
            .value("green", green)
            .value("blue", blue);
}

12.指针

只关注返回指针

参数指针不用处理

return_value_policy:返回策略

manage_new_object:指针返回策略

#include <boost/python.hpp>

using namespace boost::python;

class Student {
public:
    //静态方法,返回当前对象的指针
    static Student *create() { return new Student; }
};

BOOST_PYTHON_MODULE (pointer) {
    class_<Student>("Student", no_init)
            //return_value_policy返回策略  
            //设置manage_new_object通过python管理c++通过new创建的对象
            .def("create", &Student::create, return_value_policy<manage_new_object>())
            .staticmethod("create");
}

13.智能指针

智能指针

#include <string>
#include <boost/python.hpp>
#include <memory>

using namespace std;
using namespace boost::python;

struct Student {
    static shared_ptr<Student> create() { return shared_ptr<Student>(new Student); }

    std::string hello() { return "Just nod if you can hear me!"; }
};

BOOST_PYTHON_MODULE (smart_ptr) {
    class_<Student, shared_ptr<Student>>("Student")
            .def("create", &Student::create)
            .staticmethod("create")
            .def("hello", &Student::hello);
}

14.pure virtual

#include <boost/python.hpp>
#include <iostream>
using namespace boost::python;
using namespace std;

class Father{
public:
    virtual void sayHello()=0;
    void haha(){
        cout<<"father haha"<<endl;
    }
};

class Son:public Father{
public:
    void sayHello()override {
        cout<<"hello"<<endl;
    }
};


BOOST_PYTHON_MODULE(purevirtual)
{
    class_<Father, boost::noncopyable>("Father",no_init)
            .def("haha",&Father::haha);
    class_<Son, bases<Father>>("Son")
    .def("sayHello",&Son::sayHello);
}

15.vector

通过模板vector_indexing_suite导出

c++中的vector不等于python中的list

Boost.python中有与python的list对应的东西,是boost::python::list

如果我们不打算修改原有的C++代码,又想调用以vector为参数的函数,该怎么办呢?

  1. 在C++代码里对以vector为参数的函数进行一层封装,封装为以boost::python::list为参数的函数,导出封装后的函数

  2. 直接导出vector<T>类型。此时vector<T>本身作为一个类型被导出给python代码,与普通的类具有同等地位

它通过模板vector_indexing_suite<std::vector<T> >()导出,自动实现了append,slice,len等方法,在python里可以像使用list那样操作这个被导出的vector类

#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <vector>

using namespace std;
using namespace boost::python;

void sayHello(vector<int> params){
    cout<<params.at(0)<<endl;
}

BOOST_PYTHON_MODULE(vector_param)
{
    //vector name
    class_<std::vector<int>>("int_vector")
            .def(vector_indexing_suite<std::vector<int>>());
    def("sayHello", sayHello);
}

16.map

map通过map_indexing_suite模板导出

#include <boost/python.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include <map>
using namespace std;
using namespace boost::python;

void sayHello(map<int,int> param){
    cout<<param.size()<<endl;
}

BOOST_PYTHON_MODULE(map_sample)
{
    class_<std::map<int,int>>("int_map")
            .def(map_indexing_suite<std::map<int,int>>());
    def("sayHello", sayHello);
}

python调用

from map_param import sayHello,int_map
m = int_map()
# insert item
m.__setitem__(10,20)
sayHello(m)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容