1.为什么要混合编程?
python的优缺点
优点:编写简单
缺点:Python的运行速度是很慢的
注意:
如果只是IO耗时,换成C++程序意义也不大
2.常用的混合编程方法
-
ctypes
ctypes是python的标准库,可以直接调用c/c++编写的动态链接库
但是C++程序员需要以C语言的调用约定来提供接口,没有类,没有重载函数,没有模板,没有C++异常
-
Cython语言(python)
一种类似于Python语言的一种新型语言编写预定功能的代码,然后将这些代码转换成为C语言编译成为Python语言可以直接调用的二进制模块。
-
boost.python
与ctypes/Cython形成鲜明的对比,boost.python倾向于让C++程序员拥有更熟悉的编程环境。
-
SWIG或SIP(c语言数据类型 映射文件)
通过编写一个接口文件,使用类似于C/C++语法——声明函数、类型的信息,然后使用特殊的工具为C/C++的代码生成Python的接口代码
3.boost::python环境搭建
-
安装和配置boost
a.安装boost
sudo apt-get install libboost-all-dev
b.创建libboost_python3.so的软连接
查找so库的位置 sudo find / -name "libboost_python-py35.so" 进入库的路径 cd /usr/lib/x86_64-linux-gnu/ 创建软连接 sudo ln -s libboost_python-py35.so libboost_python3.so
如果出现如下问题:
fatal error: pyconfig.h: No such file or directory
解决办法:
a.找到pyconfig.h
文件位置
locate pyconfig.h
b.添加路径到环境变量
编辑.bashrc
添加如下内容(路径为查找到的路径)
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++的东西在环境变量中估计都是头文件
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;
//定义的函数
char const* greet()
{
return "hello, world";
}
//宏对需要导出的函数、全局变量、类等导入Python的Module_Name模块
BOOST_PYTHON_MODULE(hello)//模块名
{
//第一个:对应的python函数名
//第二个:需要导出的c++函数名
def("greet", greet);
}
- 生成python模块so
g++ -shared -o hello.so -fPIC -I /usr/include/python3.4m/ main.cpp -lpython3.4m -lboost_python3
参数含义:
-shared // 指定生成动态链接库
-o // 生成的动态链接库的名称
-fPIC // 表示使用地址无关代码
-I(大写的i) // 表示将/usr/include/python3.4m/目录作为第一个寻找头文件的目录(主要查找pyconfig.h,如果配置了环境变量可以不指定)
-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.结构体和对象
代码
BOOST_PYTHON_MODULE(class_example){
class_<Student>("Student")
.def("sayHello",&Student::sayHello);
}
6.对象的构造函数
BOOST_PYTHON_MODULE(constructor){
class_<Student>("Student")//无参
.def(init<string>())//一个参数
.def(init<string,int>())//两个参数
.def("sayHello",&Student::sayHello);
}
注意:没有构造函数 可以使用
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();
//设置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.多态
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")//没有拷贝构造
.def("name", &Base::name);
//对于继承,则需指明继承对象
class_<Derived, bases<Base> >("Derived");
def("fb", fb);
def("fd", fd);
}
9.静态成员和静态方法
指定静态方法
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调用:
from static_example import Example
# 调用静态方法
Example.sayHello()
# 调用静态属性
print(Example.name)
注意:
对于静态属性不用特别处理,静态方法需要特殊处理
10.方法重载
对于方法重载分为两种:
1.普通方法重载
2.默认参数函数(类成员以及普通函数)
代码:
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<<"调用了默认参数的函数"<<endl;
}
};
//普通默认参数函数
void hehe(string name,int age=1,double score = 1.123){
cout<<"调用了sayHello"<<name<<age<<score<<endl;
}
处理结果:
//处理默认参数函数的重载问题
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)
//默认参数的方法重载
.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.指针
只关注返回指针
参数指针不用处理
return_value_policy:返回策略
manage_new_object:指针返回策略
#include <boost/python.hpp>
using namespace boost::python;
class Student {
public:
//静态方法,返回当前对象的指针
static Student *create() { return new Student; }
};
BOOST_PYTHON_MODULE (pointer) {
class_<Student>("Student", no_init)
//return_value_policy返回策略
//设置manage_new_object通过python管理c++通过new创建的对象
.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
导出
c++中的vector不等于python中的list
Boost.python中有与python的list对应的东西,是boost::python::list
如果我们不打算修改原有的C++代码,又想调用以vector为参数的函数,该怎么办呢?
在C++代码里对以vector为参数的函数进行一层封装,封装为以boost::python::list为参数的函数,导出封装后的函数
直接导出vector<T>类型。此时vector<T>本身作为一个类型被导出给python代码,与普通的类具有同等地位
它通过模板vector_indexing_suite<std::vector<T> >()导出,自动实现了append,slice,len等方法,在python里可以像使用list那样操作这个被导出的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
模板导出
#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调用
from map_param import sayHello,int_map
m = int_map()
# insert item
m.__setitem__(10,20)
sayHello(m)