溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何利用Boost.Python實(shí)現(xiàn)Python C/C++混合編程詳解

發(fā)布時(shí)間:2020-09-06 01:06:16 來(lái)源:腳本之家 閱讀:152 作者:機(jī)器學(xué)習(xí)的小學(xué)生 欄目:開(kāi)發(fā)技術(shù)

前言

學(xué)習(xí)中如果碰到問(wèn)題,參考官網(wǎng)例子:

D:\boost_1_61_0\libs\python\test

參考:Boost.Python 中英文文檔。

利用Boost.Python實(shí)現(xiàn)Python C/C++混合編程

關(guān)于python與C++混合編程,事實(shí)上有兩個(gè)部分

  • extending 所謂python 程序中調(diào)用c/c++代碼, 其實(shí)是先處理c++代碼, 預(yù)先生成的動(dòng)態(tài)鏈接庫(kù), 如example.so, 而在python代碼中import example;即可使用c/c++的函數(shù) .
  • embedding c++代碼中調(diào)用 python 代碼.

兩者都可以用 python c 轉(zhuǎn)換api,解決,具體可以去python官方文檔查閱,但是都比較繁瑣.

對(duì)于1,extending,常用的方案是boost.python以及swig.

swig是一種膠水語(yǔ)言,粘合C++,PYTHON,我前面的圖形顯示二叉樹(shù)的文章中提到的就是利用pyqt作界面,調(diào)用c++代碼使用swig生成的.so動(dòng)態(tài)庫(kù).

而boost.python則直接轉(zhuǎn)換,可以利用py++自動(dòng)生成需要的wrapper.關(guān)于這方面的內(nèi)容的入門除了boost.python官網(wǎng),中文的入門資料推薦

下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧

導(dǎo)出函數(shù)

#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()

導(dǎo)出類:

導(dǎo)出默認(rèn)構(gòu)造的函數(shù)的類

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) //導(dǎo)出的module 名字
{
 class_<World>("World")
 .def("greet", &World::greet)
 .def("set", &World::set);
}

python:

import hello 
planet = hello.World() # 調(diào)用默認(rèn)構(gòu)造函數(shù),產(chǎn)生類對(duì)象
planet.set("howdy") # 調(diào)用對(duì)象的方法
print planet.greet() # 調(diào)用對(duì)象的方法

構(gòu)造函數(shù)的導(dǎo)出:

#include<string>
#include<boost/python.hpp>

using namespace std;
using namespace boost::python;

struct World
{
 World(string msg):msg(msg){} //增加構(gòu)造函數(shù)
 World(double a, double b):a(a),b(b) {} //另外一個(gè)構(gòu)造函數(shù)
 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) //導(dǎo)出的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 測(cè)試調(diào)用:

import hello
planet = hello.World(5,6)
planet2 = hello.World("hollo world")

print planet.sum_s()
print planet2.greet()

如果不想導(dǎo)出任何構(gòu)造函數(shù),則使用no_init:

class_<Abstract>("Abstract",no_init)

類的數(shù)據(jù)成員

#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調(diào)用:

import hello_var

var = hello_var.Var("hello_var")
var.value = 3.14
# var.name = 'hello' # error
print var.name

C++類對(duì)象導(dǎo)出為Python的類對(duì)象,注意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) // 對(duì)外:只讀
 .add_property("value", &Num::get, &Num::set);// 對(duì)外讀寫 .value值會(huì)改變.rovalue值,存儲(chǔ)著同樣的數(shù)據(jù)。

}

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; }

/*
 下面的額外的代碼如果去掉會(huì)報(bào)錯(cuò)。
 解決地址: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)

類的虛函數(shù):

/*
 類的虛函數(shù),實(shí)現(xiàn)的功能是:可以編寫Python類,來(lái)繼承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(); //如果函數(shù)進(jìn)行重載了,則返回重載的
 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()

類的運(yùn)算符/特殊函數(shù)

// 類的運(yùn)算符/特殊函數(shù)

#include<string>
#include<iostream>


// #include<boost/python.hpp> 如果僅包含該頭文件,會(huì)出錯(cuò)

#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; };//重載類型轉(zhuǎn)換符
 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>()))模板后面要加上括號(hào)。也要注意頭文件的包含,否則會(huì)引發(fā)錯(cuò)誤。

python:

import hello_operator

filepos1 = hello_operator.FilePos()
filepos1.len = 10

filepos2 = hello_operator.FilePos()
filepos2.len = 20;

print filepos1 - filepos2

函數(shù)

函數(shù)的調(diào)用策略。

// 函數(shù)的調(diào)用策略

#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; //因?yàn)閤是y的數(shù)據(jù)成員,x的聲明周期與y進(jìn)行了綁定。因?yàn)槲覀兊哪康氖牵篜ython接口應(yīng)盡可能的反映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 表示返回的值與第一個(gè)參數(shù)有關(guān)系:即第一個(gè)參數(shù)是返回對(duì)象的擁有者(y和x都是引用的形式)。
 // with_custodian_and_ward<1, 2> 表示第二個(gè)參數(shù)的生命周期依賴于第一個(gè)參數(shù)的生命周期。
 def("f", f, return_internal_reference<1, with_custodian_and_ward<1, 2> >());
}

函數(shù)重載

// 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)

默認(rèn)參數(shù)

普通函數(shù)的默認(rèn)參數(shù):

然而通過(guò)上面的方式對(duì)重載函數(shù)進(jìn)行封裝時(shí),就丟失了默認(rèn)參數(shù)的信息。當(dāng)然我們可以通過(guò)一般形式的封裝,如下:

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); // 所有參數(shù)
def("f",f2); //兩個(gè)參數(shù)
def("f",f1); //一個(gè)參數(shù)

但是通過(guò)上面的形式封裝很麻煩。我們可以通過(guò)宏的形式,為我們批量完成上面的功能。

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); // 參數(shù)個(gè)數(shù)的最小為1,最大為4

BOOST_PYTHON_MODULE(hello_overloaded)
{

 def("foo", foo, foo_overloads()); //實(shí)現(xiàn)導(dǎo)出帶有默認(rèn)參數(shù)的函數(shù)

}

python:

import hello_overloaded


hello_overloaded.foo(1)

hello_overloaded.foo(1,chr(2))

hello_overloaded.foo(1,chr(2),3) # 3對(duì)應(yīng)的C++為unsigned int

hello_overloaded.foo(1,chr(2),3,double(4))

成員函數(shù)的默認(rèn)參數(shù):

//使用BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS 宏,完成成員函數(shù)默認(rèn)參數(shù)的接口

#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); // 參數(shù)個(gè)數(shù)的最小為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實(shí)現(xiàn)構(gòu)造函數(shù)的重載。

使用方法如下:

// 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

}

對(duì)象接口

Python 是動(dòng)態(tài)類型的語(yǔ)言,C++是靜態(tài)類型的。Python變量可能是:integer,float ,list ,dict,tuple,str,long,等等,還有其他類型。從Boost.Python和C++的觀點(diǎn)來(lái)看,Python中的變量是類object的實(shí)例,在本節(jié),我們看一下如何處理Python對(duì)象。

基本接口

// 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數(shù)組對(duì)象,讓C++進(jìn)行處理
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 調(diào)用:

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

總結(jié):

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI