欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-08 21:59:28
前言 学习中如果碰到问题,参考官网例子: d:\boost_1_61_0\libs\python\test 参考:boost.python 中英文文档。 利...

前言

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

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://*.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

总结:

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