如何利用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中的二维数组的操作方法
May 02 Python
举例讲解Python中的Null模式与桥接模式编程
Feb 02 Python
Python实现的多进程和多线程功能示例
May 29 Python
用Python一键搭建Http服务器的方法
Jun 01 Python
详解Appium+Python之生成html测试报告
Jan 04 Python
pyqt5 使用label控件实时显示时间的实例
Jun 14 Python
python删除文件夹下相同文件和无法打开的图片
Jul 16 Python
python打造爬虫代理池过程解析
Aug 15 Python
python 中的paramiko模块简介及安装过程
Feb 29 Python
Python实现查找数据库最接近的数据
Jun 08 Python
解决pip安装的第三方包在PyCharm无法导入的问题
Oct 15 Python
python 装饰器重要在哪
Feb 14 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
用PHP4访问Oracle815
2006/10/09 PHP
php 字符串中的\n换行符无效、不能换行的解决方法
2014/04/02 PHP
windwos下使用php连接oracle数据库的过程分享
2014/05/26 PHP
PHP经典面试题集锦
2015/03/19 PHP
PHP实现在数据库百万条数据中随机获取20条记录的方法
2017/04/19 PHP
PHP实现搜索时记住状态的方法示例
2018/05/11 PHP
addRule在firefox下的兼容写法
2006/11/30 Javascript
使用jquery解析XML的方法
2014/09/05 Javascript
前端必备神器 Snap.svg 弹动效果
2014/11/10 Javascript
JS实现左右无缝轮播图代码
2016/05/01 Javascript
jQuery表单对象属性过滤选择器实例详解
2016/09/13 Javascript
从零学习node.js之详解异步控制工具async(八)
2017/02/27 Javascript
nodejs集成sqlite使用示例
2017/06/05 NodeJs
微信小程序input框中加入小图标的实现方法
2018/06/19 Javascript
vue-cli初始化项目中使用less的方法
2018/08/09 Javascript
JavaScript数据结构与算法之二叉树插入节点、生成二叉树示例
2019/02/21 Javascript
bootstrap table插件动态加载表头
2019/07/19 Javascript
JavaScript实现英语单词题库
2019/12/24 Javascript
vue+iview实现分页及查询功能
2020/11/17 Vue.js
ES2020让代码更优美的运算符 (?.) (??)
2021/01/04 Javascript
为Python的web框架编写MVC配置来使其运行的教程
2015/04/30 Python
Python进程间通信Queue实例解析
2018/01/25 Python
python中自带的三个装饰器的实现
2019/11/08 Python
Python3爬虫中关于中文分词的详解
2020/07/29 Python
详解python对象之间的交互
2020/09/29 Python
园林技术个人的自我评价
2014/02/15 职场文书
产假请假条
2014/04/10 职场文书
高三霸气励志标语
2014/06/24 职场文书
群教班子对照检查材料
2014/08/26 职场文书
歌颂党的演讲稿
2014/09/10 职场文书
上班迟到检讨书
2014/09/15 职场文书
滞留工资返还协议书
2014/10/19 职场文书
事业单位考察材料范文
2014/12/25 职场文书
优秀教师工作总结2015
2015/07/22 职场文书
班主任寄语2016
2015/12/04 职场文书
如何利用opencv判断两张图片是否相同详解
2021/07/07 Python