如何利用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如何使用AIML搭建聊天机器人
Jan 27 Python
理解Python中的绝对路径和相对路径
Aug 30 Python
基于Django的ModelForm组件(详解)
Dec 07 Python
python微信公众号开发简单流程
Mar 23 Python
浅谈Pycharm中的Python Console与Terminal
Jan 17 Python
python输出决策树图形的例子
Aug 09 Python
python使用celery实现异步任务执行的例子
Aug 28 Python
在pycharm中配置Anaconda以及pip源配置详解
Sep 09 Python
Pandas实现dataframe和np.array的相互转换
Nov 30 Python
Pytest测试框架基本使用方法详解
Nov 25 Python
聊聊pytorch测试的时候为何要加上model.eval()
May 23 Python
python+opencv实现视频抽帧示例代码
Jun 11 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 神盾解密工具
2014/06/08 PHP
PHP分享图片的生成方法
2018/04/25 PHP
PHP基于curl模拟post提交json数据示例
2018/06/22 PHP
JQuery 学习技巧总结
2010/05/21 Javascript
jquery 插件开发备注
2010/08/27 Javascript
兼容IE与firefox火狐的回车事件(js与jquery)
2010/10/20 Javascript
在IE和VB中支持png图片透明效果的实现方法(vb源码打包)
2011/04/01 Javascript
javascript时区函数介绍
2012/09/14 Javascript
JS Pro-深入面向对象的程序设计之继承的详解
2013/05/07 Javascript
解析ScrollPic在ie8下只滚动一遍,然后变为空白 ie6,ie7,chrome,firefox正常
2013/06/26 Javascript
javascript中style.left和offsetLeft的用法说明
2014/03/07 Javascript
jQuery animate()实现背景色渐变效果的处理方法【使用jQuery.color.js插件】
2017/03/15 Javascript
canvas基础绘制-绚丽倒计时的实例
2017/09/17 Javascript
Bootstrap 模态框多次显示后台提交多次BUG的解决方法
2017/12/26 Javascript
JS中promise化微信小程序api
2018/04/12 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【圆形情况】
2018/12/13 Javascript
实现一个Vue自定义指令懒加载的方法示例
2020/06/04 Javascript
JS实现简易贪吃蛇游戏
2020/08/24 Javascript
基于vue实现微博三方登录流程解析
2020/11/04 Javascript
Python各类图像库的图片读写方式总结(推荐)
2018/02/23 Python
python3 图片referer防盗链的实现方法
2018/03/12 Python
Python爬虫逆向分析某云音乐加密参数的实例分析
2020/12/04 Python
Python3+Appium安装及Appium模拟微信登录方法详解
2021/02/16 Python
HTML5离线缓存Manifest是什么
2016/03/09 HTML / CSS
HTML5 Canvas玩转酷炫大波浪进度图效果实例(附demo)
2016/12/14 HTML / CSS
美国隐形眼镜销售网站:ContactsDirect
2017/10/28 全球购物
澳大利亚领先的皮肤诊所:Skin Matrix(抗衰老、痤疮专家、药妆护肤)
2018/05/20 全球购物
C#里面如何判断一个Object是否是某种类型(如Boolean)?
2016/02/10 面试题
2014全国两会心得体会
2014/03/17 职场文书
新闻学专业职业生涯规划范文:我的人生我做主
2014/09/12 职场文书
国庆65周年演讲稿:回首往昔,展望未来
2014/09/21 职场文书
出纳岗位职责范本
2015/03/31 职场文书
《神奇的鸟岛》教学反思
2016/02/22 职场文书
background-position百分比原理详解
2021/05/08 HTML / CSS
Java+swing实现抖音上的表白程序详解
2022/06/25 Java/Android
Python中tqdm的使用和例子
2022/09/23 Python