1.為什么要混合編程?
python的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):編寫簡單
缺點(diǎn):Python的運(yùn)行速度是很慢的
注意:
如果只是IO耗時,換成C++程序意義也不大
2.常用的混合編程方法
-
ctypes
ctypes是python的標(biāo)準(zhǔn)庫,可以直接調(diào)用c/c++編寫的動態(tài)鏈接庫
但是C++程序員需要以C語言的調(diào)用約定來提供接口,沒有類,沒有重載函數(shù),沒有模板,沒有C++異常
-
Cython語言(python)
一種類似于Python語言的一種新型語言編寫預(yù)定功能的代碼,然后將這些代碼轉(zhuǎn)換成為C語言編譯成為Python語言可以直接調(diào)用的二進(jìn)制模塊。
-
boost.python
與ctypes/Cython形成鮮明的對比,boost.python傾向于讓C++程序員擁有更熟悉的編程環(huán)境。
-
SWIG或SIP(c語言數(shù)據(jù)類型 映射文件)
通過編寫一個接口文件,使用類似于C/C++語法——聲明函數(shù)、類型的信息,然后使用特殊的工具為C/C++的代碼生成Python的接口代碼
3.boost::python環(huán)境搭建
-
安裝和配置boost
a.安裝boost
sudo apt-get install libboost-all-devb.創(chuàng)建libboost_python3.so的軟連接
查找so庫的位置 sudo find / -name "libboost_python-py35.so" 進(jìn)入庫的路徑 cd /usr/lib/x86_64-linux-gnu/ 創(chuàng)建軟連接 sudo ln -s libboost_python-py35.so libboost_python3.so 如果出現(xiàn)如下問題:
fatal error: pyconfig.h: No such file or directory
解決辦法:
a.找到pyconfig.h文件位置
locate pyconfig.h
b.添加路徑到環(huán)境變量
編輯.bashrc添加如下內(nèi)容(路徑為查找到的路徑)
export CPLUS_INCLUDE_PATH=/usr/include/python3.4m
也可以配置anconda中的
export CPLUS_INCLUDE_PATH=/home/cai/anaconda3/pkgs/python-3.6.2-0/include/python3.6m
這個配置的是頭文件,我想c++的東西在環(huán)境變量中估計(jì)都是頭文件
python版本含義:
--with-pydebug (flag: d)
--with-pymalloc (flag: m)
--with-wide-unicode (flag: u)
4.helloworld
1.代碼
// 必須包含這個頭文件
#include <boost/python.hpp>
// 使用boost:python的命名空間
using namespace boost::python;
//定義的函數(shù)
char const* greet()
{
return "hello, world";
}
//宏對需要導(dǎo)出的函數(shù)、全局變量、類等導(dǎo)入Python的Module_Name模塊
BOOST_PYTHON_MODULE(hello)//模塊名
{
//第一個:對應(yīng)的python函數(shù)名
//第二個:需要導(dǎo)出的c++函數(shù)名
def("greet", greet);
}
- 生成python模塊so
g++ -shared -o hello.so -fPIC -I /usr/include/python3.4m/ main.cpp -lpython3.4m -lboost_python3
參數(shù)含義:
-shared // 指定生成動態(tài)鏈接庫
-o // 生成的動態(tài)鏈接庫的名稱
-fPIC // 表示使用地址無關(guān)代碼
-I(大寫的i) // 表示將/usr/include/python3.4m/目錄作為第一個尋找頭文件的目錄(主要查找pyconfig.h,如果配置了環(huán)境變量可以不指定)
-l // 指定需鏈接的庫名
4.通過clion生成python模塊
配置CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(BoostPython)
set(CMAKE_CXX_STANDARD 14)
add_definitions(-fPIC)
# pyconfig.h的路徑
include_directories(/usr/include/python3.4m)
# 注意庫名稱要和代碼中指定的一致
add_library(hello SHARED main.cpp)
# 連接
target_link_libraries(
hello
python3.4m
boost_python3
)
5.結(jié)構(gòu)體和對象
代碼
BOOST_PYTHON_MODULE(class_example){
class_<Student>("Student")
.def("sayHello",&Student::sayHello);
}
6.對象的構(gòu)造函數(shù)
BOOST_PYTHON_MODULE(constructor){
class_<Student>("Student")//無參
.def(init<string>())//一個參數(shù)
.def(init<string,int>())//兩個參數(shù)
.def("sayHello",&Student::sayHello);
}
注意:沒有構(gòu)造函數(shù) 可以使用
no_init表示
7.成員變量
def_readonly:只讀屬性
def_readwrite:讀寫屬性
add_property:將get和set方法包裝成屬性
代碼
class Student {
private:
double mNumber;
public:
//不可修改
const string name;
//可讀可寫
int age;
Student();
~Student();
//獲取mNumber
double getNumber();
//設(shè)置mNumber
void setNumber(double value);
//打印三個屬性
void printMembers();
};
可以把getNumber和setNumber包裝成一個屬性
包裝:
BOOST_PYTHON_MODULE(members){
class_<Student>("Student")
.def_readonly("name",&Student::name)
.def_readwrite("age",&Student::age)
//必須要先get再set
.add_property("number",&Student::getNumber,&Student::setNumber)
.def("printMembers",&Student::printMembers);
}
8.多態(tài)
class Base
{
public:
virtual std::string name() const { return "Base"; }
virtual ~Base() {}
};
class Derived : public Base
{
public:
virtual std::string name() const { return "Derived"; }
};
void fb(Base *b)
{
std::cout << b->name() << " called." << std::endl;
}
void fd(Derived *d)
{
std::cout << "Derived " << d->name() << " called." << std::endl;
}
包裝:
BOOST_PYTHON_MODULE(inheritance)
{
class_<Base, boost::noncopyable>("Base")//沒有拷貝構(gòu)造
.def("name", &Base::name);
//對于繼承,則需指明繼承對象
class_<Derived, bases<Base> >("Derived");
def("fb", fb);
def("fd", fd);
}
9.靜態(tài)成員和靜態(tài)方法
指定靜態(tài)方法
staticmethod
#include <iostream>
using namespace std;
class Example {
public:
static string name;
static void sayHello();
};
string Example::name = "張三";
void Example::sayHello(){
cout<<"hello world"<<endl;
}
#include <boost/python.hpp>
using namespace boost::python;
boost包裝:
BOOST_PYTHON_MODULE (static_example) {
class_<Example>("Example", no_init)
.def("sayHello", &Example::sayHello)
.def_readwrite("name",&Example::name)
.staticmethod("sayHello");
}
python調(diào)用:
from static_example import Example
# 調(diào)用靜態(tài)方法
Example.sayHello()
# 調(diào)用靜態(tài)屬性
print(Example.name)
注意:
對于靜態(tài)屬性不用特別處理,靜態(tài)方法需要特殊處理
10.方法重載
對于方法重載分為兩種:
1.普通方法重載
2.默認(rèn)參數(shù)函數(shù)(類成員以及普通函數(shù))
代碼:
class Student{
public:
void sayHello(){
cout<<"空 sayHello"<<endl;
}
void sayHello(int age){
cout<<"int sayHello"<<endl;
}
void sayHello(string name,int age){
cout<<"string和int sayHello"<<endl;
}
void haha(string name,int age=10,double score = 34){
cout<<"調(diào)用了默認(rèn)參數(shù)的函數(shù)"<<endl;
}
};
//普通默認(rèn)參數(shù)函數(shù)
void hehe(string name,int age=1,double score = 1.123){
cout<<"調(diào)用了sayHello"<<name<<age<<score<<endl;
}
處理結(jié)果:
//處理默認(rèn)參數(shù)函數(shù)的重載問題
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(haha_overloads, haha, 1, 3)
//處理普通方法重載
BOOST_PYTHON_FUNCTION_OVERLOADS(sayHello_overloads, hehe, 1, 3)
BOOST_PYTHON_MODULE(overload)
{
//普通重載的方法
void (Example::*d1)() = &Example::doit;
std::string (Example::*d2)(unsigned int) = &Example::doit;
void (Example::*d3)(std::string) = &Example::doit;
class_<Example>("Example")
.def("__str__", &Example::print)
.def("doit", d1)
.def("doit", d2)
.def("doit", d3)
//默認(rèn)參數(shù)的方法重載
.def("makeIt", &Example::makeIt, makeIt_overloads());
}
11.枚舉
使用enum_包裝枚舉
格式:
enum_<name>("name")
.value("value1", value1)
.value("value2", value2)
.value("value3", value3);
boost封裝:
#include <boost/python.hpp>
using namespace boost::python;
enum color { red = 1, green = 2, blue = 4 };
BOOST_PYTHON_MODULE(enum_example)
{
enum_<color>("color")
.value("red", red)
.value("green", green)
.value("blue", blue);
}
12.指針
只關(guān)注返回指針
參數(shù)指針不用處理
return_value_policy:返回策略
manage_new_object:指針返回策略
#include <boost/python.hpp>
using namespace boost::python;
class Student {
public:
//靜態(tài)方法,返回當(dāng)前對象的指針
static Student *create() { return new Student; }
};
BOOST_PYTHON_MODULE (pointer) {
class_<Student>("Student", no_init)
//return_value_policy返回策略
//設(shè)置manage_new_object通過python管理c++通過new創(chuàng)建的對象
.def("create", &Student::create, return_value_policy<manage_new_object>())
.staticmethod("create");
}
13.智能指針
智能指針
#include <string>
#include <boost/python.hpp>
#include <memory>
using namespace std;
using namespace boost::python;
struct Student {
static shared_ptr<Student> create() { return shared_ptr<Student>(new Student); }
std::string hello() { return "Just nod if you can hear me!"; }
};
BOOST_PYTHON_MODULE (smart_ptr) {
class_<Student, shared_ptr<Student>>("Student")
.def("create", &Student::create)
.staticmethod("create")
.def("hello", &Student::hello);
}
14.pure virtual
#include <boost/python.hpp>
#include <iostream>
using namespace boost::python;
using namespace std;
class Father{
public:
virtual void sayHello()=0;
void haha(){
cout<<"father haha"<<endl;
}
};
class Son:public Father{
public:
void sayHello()override {
cout<<"hello"<<endl;
}
};
BOOST_PYTHON_MODULE(purevirtual)
{
class_<Father, boost::noncopyable>("Father",no_init)
.def("haha",&Father::haha);
class_<Son, bases<Father>>("Son")
.def("sayHello",&Son::sayHello);
}
15.vector
通過模板
vector_indexing_suite導(dǎo)出
c++中的vector不等于python中的list
Boost.python中有與python的list對應(yīng)的東西,是boost::python::list
如果我們不打算修改原有的C++代碼,又想調(diào)用以vector為參數(shù)的函數(shù),該怎么辦呢?
在C++代碼里對以vector為參數(shù)的函數(shù)進(jìn)行一層封裝,封裝為以boost::python::list為參數(shù)的函數(shù),導(dǎo)出封裝后的函數(shù)
直接導(dǎo)出vector<T>類型。此時vector<T>本身作為一個類型被導(dǎo)出給python代碼,與普通的類具有同等地位
它通過模板vector_indexing_suite<std::vector<T> >()導(dǎo)出,自動實(shí)現(xiàn)了append,slice,len等方法,在python里可以像使用list那樣操作這個被導(dǎo)出的vector類
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <vector>
using namespace std;
using namespace boost::python;
void sayHello(vector<int> params){
cout<<params.at(0)<<endl;
}
BOOST_PYTHON_MODULE(vector_param)
{
//vector name
class_<std::vector<int>>("int_vector")
.def(vector_indexing_suite<std::vector<int>>());
def("sayHello", sayHello);
}
16.map
map通過
map_indexing_suite模板導(dǎo)出
#include <boost/python.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include <map>
using namespace std;
using namespace boost::python;
void sayHello(map<int,int> param){
cout<<param.size()<<endl;
}
BOOST_PYTHON_MODULE(map_sample)
{
class_<std::map<int,int>>("int_map")
.def(map_indexing_suite<std::map<int,int>>());
def("sayHello", sayHello);
}
python調(diào)用
from map_param import sayHello,int_map
m = int_map()
# insert item
m.__setitem__(10,20)
sayHello(m)