如何利用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实现根据指定端口探测服务器/模块部署的方法
Aug 25 Python
解决python3 json数据包含中文的读写问题
May 10 Python
pyqt5的QComboBox 使用模板的具体方法
Sep 06 Python
对Python模块导入时全局变量__all__的作用详解
Jan 11 Python
Python中new方法的详解
Jan 15 Python
Python实现定时执行任务的三种方式简单示例
Mar 30 Python
python原类、类的创建过程与方法详解
Jul 19 Python
Python Selenium截图功能实现代码
Apr 26 Python
django admin 根据choice字段选择的不同来显示不同的页面方式
May 13 Python
Pycharm中配置远程Docker运行环境的教程图解
Jun 11 Python
Python使用Opencv实现边缘检测以及轮廓检测的实现
Dec 31 Python
matplotlib交互式数据光标mpldatacursor的实现
Feb 03 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
MYSQL环境变量设置方法
2007/01/15 PHP
PHP下10件你也许并不了解的事情
2008/09/11 PHP
刷新PHP缓冲区为你的站点加速
2015/10/10 PHP
PHP中__autoload和Smarty冲突的简单解决方法
2016/04/08 PHP
Yii2框架使用计划任务的方法
2016/05/25 PHP
PHP获取页面执行时间的方法(推荐)
2016/12/10 PHP
Yii2中datetime类的使用
2016/12/17 PHP
php-fpm添加service服务的例子
2018/04/27 PHP
数据结构之利用PHP实现二分搜索树
2020/10/25 PHP
js 加载并解析XML字符串的代码
2009/12/13 Javascript
JQUERY 获取IFrame中对象及获取其父窗口中对象示例
2013/08/19 Javascript
无闪烁更新网页内容JS实现
2013/12/19 Javascript
seajs中模块的解析规则详解和模块使用总结
2014/03/12 Javascript
AngularJS使用ng-repeat指令实现下拉框
2016/08/23 Javascript
JS动态生成年份和月份实例代码
2017/02/04 Javascript
ES6新特性五:Set与Map的数据结构实例分析
2017/04/21 Javascript
webpack2.0配置postcss-loader的方法
2017/08/17 Javascript
轻量级JS Cookie插件js-cookie的使用方法
2018/03/22 Javascript
基于vue.js中关于下拉框的值默认及绑定问题
2018/08/22 Javascript
Vue.js 十五分钟入门图文教程
2018/09/12 Javascript
使用electron制作满屏心特效的示例代码
2018/11/27 Javascript
vue不操作dom实现图片轮播的示例代码
2019/12/18 Javascript
JS常见错误(Error)及处理方案详解
2020/07/02 Javascript
vue+iview实现分页及查询功能
2020/11/17 Vue.js
[46:28]EG vs Liquid 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.23
2019/09/05 DOTA
pandas修改DataFrame列名的实现方法
2019/02/22 Python
Abe’s of Maine:自1979以来销售相机和电子产品
2016/11/21 全球购物
巴西24小时在线药房:Droga Raia
2020/05/12 全球购物
PHP如何设置和取得Cookie值
2015/06/30 面试题
杭州联环马网络笔试题面试题
2013/08/04 面试题
高一家长会邀请函
2014/01/12 职场文书
大学生志愿者活动总结
2014/06/27 职场文书
大卫科波菲尔读书笔记
2015/06/30 职场文书
python函数指定默认值的实例讲解
2021/03/29 Python
OpenCV中resize函数插值算法的实现过程(五种)
2021/06/05 Python
2021年国产动漫公司排行前十名,玄机科技上榜,第二推出过铠甲勇士
2022/03/18 杂记