如何利用Boost.Python实现Python C/C++混合编程详解


Posted in Python onNovember 08, 2018

前言

学习中如果碰到问题,参考官网例子:

D:\boost_1_61_0\libs\python\test

参考:Boost.Python 中英文文档。

利用Boost.Python实现Python C/C++混合编程

关于python与C++混合编程,事实上有两个部分

  • extending 所谓python 程序中调用c/c++代码, 其实是先处理c++代码, 预先生成的动态链接库, 如example.so, 而在python代码中import example;即可使用c/c++的函数 .
  • embedding c++代码中调用 python 代码.

两者都可以用 python c 转换api,解决,具体可以去python官方文档查阅,但是都比较繁琐.

对于1,extending,常用的方案是boost.python以及swig.

swig是一种胶水语言,粘合C++,PYTHON,我前面的图形显示二叉树的文章中提到的就是利用pyqt作界面,调用c++代码使用swig生成的.so动态库.

而boost.python则直接转换,可以利用py++自动生成需要的wrapper.关于这方面的内容的入门除了boost.python官网,中文的入门资料推荐

下面话不多说了,来一起看看详细的介绍吧

导出函数

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

using namespace std;
using namespace boost::python;


char const * greet()
{
 return "hello,world";

}

BOOST_PYTHON_MODULE(hello_ext)
{
 def("greet", greet);
}

python:

import hello_ext
print hello_ext.greet()

导出类:

导出默认构造的函数的类

c++

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

using namespace std;
using namespace boost::python;

struct World
{
 void set(string msg) { this->msg = msg; }
 string greet() { return msg; }

 string msg;
};

BOOST_PYTHON_MODULE(hello) //导出的module 名字
{
 class_<World>("World")
 .def("greet", &World::greet)
 .def("set", &World::set);
}

python:

import hello 
planet = hello.World() # 调用默认构造函数,产生类对象
planet.set("howdy") # 调用对象的方法
print planet.greet() # 调用对象的方法

构造函数的导出:

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

using namespace std;
using namespace boost::python;

struct World
{
 World(string msg):msg(msg){} //增加构造函数
 World(double a, double b):a(a),b(b) {} //另外一个构造函数
 void set(string msg) { this->msg = msg; }
 string greet() { return msg; }
 double sum_s() { return a + b; }
 string msg;
 double a;
 double b;
};

BOOST_PYTHON_MODULE(hello) //导出的module 名字
{
 class_<World>("World",init<string>()) 
 .def(init<double,double>()) // expose another construct
 .def("greet", &World::greet)
 .def("set", &World::set)
 .def("sum_s", &World::sum_s);
}

python 测试调用:

import hello
planet = hello.World(5,6)
planet2 = hello.World("hollo world")

print planet.sum_s()
print planet2.greet()

如果不想导出任何构造函数,则使用no_init:

class_<Abstract>("Abstract",no_init)

类的数据成员

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

using namespace std;
using namespace boost::python;


struct Var
{
 Var(string name):name(name),value(){}
 string const name;

 float value;
};

BOOST_PYTHON_MODULE(hello_var)
{
 class_<Var>("Var", init<string>())
 .def_readonly("name", &Var::name) //只读
 .def_readwrite("value", &Var::value); //读写
}

python调用:

import hello_var

var = hello_var.Var("hello_var")
var.value = 3.14
# var.name = 'hello' # error
print var.name

C++类对象导出为Python的类对象,注意var.name不能赋值。

类的属性

// 类的属性

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

using namespace std;
using namespace boost::python;


struct Num
{
 Num(){}
 float get() const { return val; }
 void set(float val) { this->val = val; }
 float val;

};

BOOST_PYTHON_MODULE(hello_num)
{
 class_<Num>("Num")
 .add_property("rovalue", &Num::get) // 对外:只读
 .add_property("value", &Num::get, &Num::set);// 对外读写 .value值会改变.rovalue值,存储着同样的数据。

}

python:

import hello_num
num = hello_num.Num()
num.value = 10
print num.rovalue # result: 10

继承

// 类的继承

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

using namespace std;
using namespace boost::python;

struct Base {
 virtual ~Base() {};
 virtual string getName() { return "Base"; }

 string str;
};

struct Derived : Base {

 string getName() { return "Derived"; }

};


void b(Base *base) { cout << base->getName() << endl; };

void d(Derived *derived) { cout << derived->getName() << endl; };

Base * factory() { return new Derived; }

/*
 下面的额外的代码如果去掉会报错。
 解决地址:http://stackoverflow.com/questions/38261530/unresolved-external-symbols-since-visual-studio-2015-update-3-boost-python-link/38291152#38291152
*/
namespace boost
{
 template <>
 Base const volatile * get_pointer<class Base const volatile >(
 class Base const volatile *c)
 {
 return c;
 }
}


BOOST_PYTHON_MODULE(hello_derived)
{
 class_<Base>("Base")
 .def("getName", &Base::getName)
 .def_readwrite("str", &Base::str);


 class_<Derived, bases<Base> >("Derived")
 .def("getName", &Derived::getName)
 .def_readwrite("str", &Derived::str);


 def("b", b);
 def("d", d);

 def("factory", factory,
 return_value_policy<manage_new_object>());//

}

python:

import hello_derived
derive = hello_derived.factory()
hello_derived.d(derive)

类的虚函数:

/*
 类的虚函数,实现的功能是:可以编写Python类,来继承C++类
*/
#include<boost/python.hpp>

#include<boost/python/wrapper.hpp>
#include<string>
#include<iostream>

using namespace boost::python;
using namespace std;

struct Base
{
 virtual ~Base() {}
 virtual int f() { return 0; };
};


struct BaseWrap : Base, wrapper<Base>
{
 int f()
 {
 if (override f = this->get_override("f"))
  return f(); //如果函数进行重载了,则返回重载的
 return Base::f(); //否则返回基类
 }
 int default_f() { return this->Base::f(); }
};

BOOST_PYTHON_MODULE(hello_virtual)
{
 class_<BaseWrap, boost::noncopyable>("Base")
 .def("f", &Base::f, &BaseWrap::default_f);


}

python:

import hello_virtual


base = hello_virtual.Base()
# 定义派生类,继承C++类
class Derived(hello_virtual.Base):
 def f(self):
 return 42

derived = Derived()


print base.f()

print derived.f()

类的运算符/特殊函数

// 类的运算符/特殊函数

#include<string>
#include<iostream>


// #include<boost/python.hpp> 如果仅包含该头文件,会出错

#include <boost/python/operators.hpp>
#include <boost/python/class.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/operators.hpp>

using namespace std;
using namespace boost::python;

class FilePos
{
public:
 FilePos() :len(0) {}
 operator double()const { return len; };//重载类型转换符
 int len;
};

// operator 方法

FilePos operator+(FilePos pos, int a)
{
 pos.len = pos.len + a;

 return pos; //返回的是副本

}

FilePos operator+(int a, FilePos pos)
{
 pos.len = pos.len + a;

 return pos; //返回的是副本

}


int operator-(FilePos pos1, FilePos pos2)
{

 return (pos1.len - pos2.len);

}

FilePos operator-(FilePos pos, int a)
{
 pos.len = pos.len - a;
 return pos;
}

FilePos &operator+=(FilePos & pos, int a)
{
 pos.len = pos.len + a;
 return pos;
}

FilePos &operator-=(FilePos & pos, int a)
{
 pos.len = pos.len - a;
 return pos;
}

bool operator<(FilePos pos1, FilePos pos2)
{
 if (pos1.len < pos2.len)
 return true;
 return false;
}


//特殊的方法

FilePos pow(FilePos pos1, FilePos pos2)
{
 FilePos res;
 res.len = std::pow(pos1.len, pos2.len);
 return res;

}
FilePos abs(FilePos pos)
{
 FilePos res;
 res.len = std::abs(pos.len);

 return res;
}

ostream& operator<<(ostream& out, FilePos pos)
{
 out << pos.len;
 return out;
}

BOOST_PYTHON_MODULE(hello_operator)
{
 class_<FilePos>("FilePos")
 .def_readwrite("len",&FilePos::len)
 .def(self + int())
 .def(int() + self)
 .def(self - self)
 .def(self - int())
 .def(self += int())
 .def(self -= other<int>())
 .def(self < self)
 .def(float_(self))//特殊方法 , __float__
 .def(pow(self, other<FilePos>())) // __pow__
 .def(abs(self))  // __abs__
 .def(str(self));  // __str__ for ostream


}

注意上面的:.def(pow(self, other<FilePos>()))模板后面要加上括号。也要注意头文件的包含,否则会引发错误。

python:

import hello_operator

filepos1 = hello_operator.FilePos()
filepos1.len = 10

filepos2 = hello_operator.FilePos()
filepos2.len = 20;

print filepos1 - filepos2

函数

函数的调用策略。

// 函数的调用策略

#include<string>
#include<iostream>

#include<boost/python.hpp>

using namespace std;
using namespace boost::python;

struct X
{
 string str;
};
struct Z
{
 int value;
};

struct Y
{
 X x;
 Z *z;
 int z_value() { return z->value; }
};

X & f(Y &y, Z*z)
{
 y.z = z;
 return y.x; //因为x是y的数据成员,x的声明周期与y进行了绑定。因为我们的目的是:Python接口应尽可能的反映C++接口
}


BOOST_PYTHON_MODULE(hello_call_policy)
{

 class_<Y>("Y")
 .def_readwrite("x", &Y::x)
 .def_readwrite("z", &Y::z)
 .def("z_value", &Y::z_value);
 class_<X>("X")
 .def_readwrite("str", &X::str);
 class_<Z>("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<string>
#include<iostream>

#include<boost/python.hpp>

using namespace std;
using namespace boost::python;

struct X
{

 bool f(int a)
 {
 return true;
 }
 bool f(int a, double b)
 {
 return true;
 }
 bool f(int a, double b, char c)
 {
 return true;
 }
 int f(int a, int b, int c)
 {
 return a + 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>("X")
 .def("f", fx1)
 .def("f", fx2)
 .def("f", fx3)
 .def("f", fx4);

}

python:

import hello_overloaded

x = hello_overloaded.X() # create a new object


print x.f(1) # default int type
print x.f(2,double(3))
print x.f(4,double(5),chr(6)) # chr(6) convert * to char 
print x.f(7,8,9)

默认参数

普通函数的默认参数:

然而通过上面的方式对重载函数进行封装时,就丢失了默认参数的信息。当然我们可以通过一般形式的封装,如下:

int f(int,double = 3.14,char const * = "hello");
int f1(int x){ return f(x);}
int f2(int x,double y){return f(x,y)}

//int module init
def("f",f); // 所有参数
def("f",f2); //两个参数
def("f",f1); //一个参数

但是通过上面的形式封装很麻烦。我们可以通过宏的形式,为我们批量完成上面的功能。

C++:

// BOOST_PYTHON_FUNCTION_OVERLOADS

#include<string>
#include<iostream>

#include<boost/python.hpp>


using namespace std;
using namespace boost::python;


void foo(int a, char b = 1, unsigned c = 2, double d = 3)
{
 return;
}

BOOST_PYTHON_FUNCTION_OVERLOADS(foo_overloads, foo, 1, 4); // 参数个数的最小为1,最大为4

BOOST_PYTHON_MODULE(hello_overloaded)
{

 def("foo", foo, foo_overloads()); //实现导出带有默认参数的函数

}

python:

import hello_overloaded


hello_overloaded.foo(1)

hello_overloaded.foo(1,chr(2))

hello_overloaded.foo(1,chr(2),3) # 3对应的C++为unsigned int

hello_overloaded.foo(1,chr(2),3,double(4))

成员函数的默认参数:

//使用BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS 宏,完成成员函数默认参数的接口

#include<string>
#include<iostream>

#include<boost/python.hpp>


using namespace std;
using namespace boost::python;

struct george
{
 void wack_em(int a, int b = 0, char c = 'x')
 {
  return;
 }

};


BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(george_overloads, wack_em, 1, 3); // 参数个数的最小为1,最大为3

BOOST_PYTHON_MODULE(hello_member_overloaded)
{

 class_<george>("george")
  .def("wack_em", &george::wack_em, george_overloads());

}

python:

import hello_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实现构造函数的重载。

使用方法如下:

// init optional

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

using namespace std;
using namespace boost::python;

struct X
{
 X(int a, char b = 'D', string c = "constructor", double b = 0.0) {}
};

BOOST_PYTHON_MODULE(hello_construct_overloaded)
{
 class_<X>("X")
  .def(init<int, optional<char, string, double> >()); // init 和 optional

}

对象接口

Python 是动态类型的语言,C++是静态类型的。Python变量可能是:integer,float ,list ,dict,tuple,str,long,等等,还有其他类型。从Boost.Python和C++的观点来看,Python中的变量是类object的实例,在本节,我们看一下如何处理Python对象。

基本接口

// init optional

#include<string>
#include<iostream>
#include<boost/python.hpp>
#include <numpy/arrayobject.h>
using namespace std;
using namespace boost::python;

namespace bp = boost::python;


void f(object x)
{
 int y = extract<int>(x); // retrieve an int from x

}

int g(object x)
{
 extract<int> get_int(x);
 if (get_int.check())
  return get_int();
 else
  return 0;
}


int test(object &x)
{
 dict d = extract<dict>(x.attr("__dict__"));
 d["whatever"] = 4;
 return 0;
}

int test2(dict & d)
{
 d["helloworld"] = 3;
 return 0;
}
class A {

public:
 list lst;
 void listOperation(list &lst) {};
};

// 传入np.array数组对象,让C++进行处理
int add_arr_1(object & data_obj, object rows_obj, object cols_obj)
{
 PyArrayObject* data_arr = reinterpret_cast<PyArrayObject*>(data_obj.ptr());
 float * data = static_cast<float *>(PyArray_DATA(data_arr));
 // using data
 int rows = extract<int>(rows_obj);
 int cols = extract<int>(cols_obj);
 for (int i = 0; i < rows*cols; i++)
 {
  data[i] += 1;
 }
 return 0;

}
BOOST_PYTHON_MODULE(hello_object)
{
 def("test", test);
 def("test2", test2);
 def("add_arr_1", add_arr_1);
}

python 调用:

import hello_object

dic1 = {"whatever":1}

hello_object.test2(dic1)

arr = np.array([1,2,3],dtype = float32)

print arr.dtype

print arr

hello_object.add_arr_1(arr,1,3)

print arr

总结:

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
python的id()函数介绍
Feb 10 Python
举例讲解Python面相对象编程中对象的属性与类的方法
Jan 19 Python
关于pip的安装,更新,卸载模块以及使用方法(详解)
May 19 Python
基于python的字节编译详解
Sep 20 Python
Python切片操作去除字符串首尾的空格
Apr 22 Python
12个步骤教你理解Python装饰器
Jul 01 Python
PyTorch 普通卷积和空洞卷积实例
Jan 07 Python
推荐8款常用的Python GUI图形界面开发框架
Feb 23 Python
在python中实现求输出1-3+5-7+9-......101的和
Apr 02 Python
分享unittest单元测试框架中几种常用的用例加载方法
Dec 02 Python
Python的scikit-image模块实例讲解
Dec 30 Python
Python使用DFA算法过滤内容敏感词
Apr 22 Python
python训练数据时打乱训练数据与标签的两种方法小结
Nov 08 #Python
对Python random模块打乱数组顺序的实例讲解
Nov 08 #Python
Python中对数组集进行按行打乱shuffle的方法
Nov 08 #Python
python绘制热力图heatmap
Mar 23 #Python
Django 路由控制的实现代码
Nov 08 #Python
Python求两个圆的交点坐标或三个圆的交点坐标方法
Nov 07 #Python
在Python中分别打印列表中的每一个元素方法
Nov 07 #Python
You might like
解析php中session的实现原理以及大网站应用应注意的问题
2013/06/17 PHP
PHPMailer的主要功能特点和简单使用说明
2014/02/17 PHP
PHP 返回数组后处理方法(开户成功后弹窗提示)
2017/07/03 PHP
ubutu 16.04环境下,PHP与mysql数据库,网页登录验证实例讲解
2017/07/20 PHP
PHP获取数组中指定的一列实例
2017/12/27 PHP
PHP中quotemeta()函数的用法讲解
2019/04/04 PHP
js如何判断不同系统的浏览器类型
2013/10/28 Javascript
NodeJS学习笔记之Connect中间件模块(二)
2015/01/27 NodeJs
js实现使用鼠标拖拽切换图片的方法
2015/05/04 Javascript
JavaScript操作选择对象的简单实例
2016/05/16 Javascript
javascript 常用验证函数总结
2016/06/28 Javascript
React Router基础使用
2017/01/17 Javascript
Angular2入门教程之模块和组件详解
2017/05/28 Javascript
underscore之function_动力节点Java学院整理
2017/07/11 Javascript
vuejs 切换导航条高亮(路由菜单高亮)的方法示例
2018/05/29 Javascript
自定义Vue组件打包、发布到npm及使用教程
2019/05/22 Javascript
了解重排与重绘
2019/05/29 Javascript
[59:35]DOTA2上海特级锦标赛主赛事日 - 3 败者组第三轮#1COL VS Alliance第二局
2016/03/04 DOTA
python中global与nonlocal比较
2014/11/21 Python
python操作字典类型的常用方法(推荐)
2016/05/16 Python
微信跳一跳python代码实现
2018/01/05 Python
使用python3调用wxpy模块监控linux日志并定时发送消息给群组或好友
2019/06/05 Python
用python3 urllib破解有道翻译反爬虫机制详解
2019/08/14 Python
swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上
2013/07/06 面试题
高中自我鉴定
2013/12/20 职场文书
医院检讨书范文
2014/02/01 职场文书
爱耳日活动总结
2014/04/30 职场文书
说明书范文
2014/05/07 职场文书
保卫钓鱼岛口号
2014/06/20 职场文书
做人民满意的公务员活动方案
2014/08/25 职场文书
2014年小学德育工作总结
2014/12/05 职场文书
推广普通话主题班会
2015/08/17 职场文书
《分数的意义》教学反思
2016/02/20 职场文书
Python类方法总结讲解
2021/07/26 Python
使用Redis实现点赞取消点赞的详细代码
2022/03/20 Redis
Python如何快速找到多个字典中的公共键(key)
2022/04/29 Python