如何利用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模块的方法详解
Sep 18 Python
python绘制圆柱体的方法
Jul 02 Python
python读取文本中的坐标方法
Oct 14 Python
python组合无重复三位数的实例
Nov 13 Python
浅析Python 读取图像文件的性能对比
Mar 07 Python
基于python生成器封装的协程类
Mar 20 Python
Python实现的删除重复文件或图片功能示例【去重】
Apr 23 Python
python 实现识别图片上的数字
Jul 30 Python
pycharm 批量修改变量名称的方法
Aug 01 Python
python的faker库用法
Nov 28 Python
django框架使用views.py的函数对表进行增删改查内容操作详解【models.py中表的创建、views.py中函数的使用,基于对象的跨表查询】
Dec 12 Python
Python中生成ndarray实例讲解
Feb 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中将数组转成XML格式的实现代码
2011/08/08 PHP
php 获取今日、昨日、上周、本月的起始时间戳和结束时间戳的方法
2013/09/28 PHP
PHP生成Gif图片验证码
2013/10/27 PHP
PHP防盗链代码实例
2014/08/27 PHP
php+mysql大量用户登录解决方案分析
2014/12/29 PHP
mysql查找删除重复数据并只保留一条实例详解
2016/09/24 PHP
php在windows环境下获得cpu内存实时使用率(推荐)
2018/02/08 PHP
PHP中检查isset()和!empty()函数的必要性
2019/02/13 PHP
thinkPHP5框架路由常用知识点汇总
2019/09/15 PHP
jquery+ajax每秒向后台发送请求数据然后返回页面的代码
2011/01/17 Javascript
Javascript面向对象编程
2012/03/18 Javascript
javascript限制文本框输入值类型的方法
2015/05/07 Javascript
JS中对象与字符串的互相转换详解
2016/05/20 Javascript
使用开源工具制作网页验证码的方法
2016/10/17 Javascript
bootstrapValidator bootstrap-select验证不可用的解决办法
2017/01/11 Javascript
nodejs+websocket实时聊天系统改进版
2017/05/18 NodeJs
JS中类的静态方法,静态变量,实例方法,实例变量区别与用法实例分析
2020/03/14 Javascript
[02:28]DOTA2英雄基础教程 灰烬之灵
2013/12/19 DOTA
[05:05]DOTA2亚洲邀请赛 战队出场仪式
2015/02/07 DOTA
[53:43]VP vs NewBee Supermajor 胜者组 BO3 第三场 6.5
2018/06/06 DOTA
[47:04]EG vs RNG 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
学习python之编写简单乘法口诀表实现代码
2016/02/27 Python
Python实现ping指定IP的示例
2018/06/04 Python
Python为何不能用可变对象作为默认参数的值
2019/07/01 Python
深入了解python列表(LIST)
2020/06/08 Python
Flask中jinja2的继承实现方法及实例
2021/03/03 Python
传播学专业毕业生自荐信
2013/11/04 职场文书
酒店出纳岗位职责
2013/12/29 职场文书
企业安全生产责任书
2014/04/14 职场文书
车队安全员岗位职责
2015/02/15 职场文书
公诉意见书范文
2015/06/05 职场文书
奶茶店的创业计划书该怎么写?
2019/07/15 职场文书
复制别人的成功真的会成功吗?
2019/10/17 职场文书
python plt.plot bar 如何设置绘图尺寸大小
2021/06/01 Python
nodejs利用readline提示输入内容实例代码
2021/07/15 NodeJs
浅谈MySql update会锁定哪些范围的数据
2022/06/25 MySQL