python调用C++--VS2019配置pybind11
一、安装pybind11
方法1,直接用pip安装:pip3 install pybind11
方法2,源代码安装:git clone https://github.com/pybind/pybind11
二、VS2019配置pybind11及使用
这里只讲解windows + vs + python使用pybind11,其他平台参加官网
1、VS2019环境配置
第一步、创建项目并调整为release/x64,右键点击工程选择属性,配置属性>>常规>>配置 设置成dll
第二步、设置输出文件,一般 pyd 的导出文件名称就是项目的名称,因此,最开始项目名称和 module 名称要一致,否则 import 时会报找不到
第三步、配置属性>>常规>>VC++目录
设置一:配置属性>>常规>>VC++目录>>包含目录:
python 的 include 目录: D:\Python\include
pybind11 的 include 目录: D:\pybind11\include
设置二:配置属性>>常规>>VC++目录>>库目录:
python 的 libs 目录:D:\Python\libs
第四步、链接器>>输入>>附加依赖项 设置python相关依赖的lib,主要用的是python36.lib 和 python3.lib
步骤五、运行测试
#include <pybind11\pybind11.h> #include <iostream> void age(double age) { std::cout << "age = " << age << std::endl; } std::string Name(std::string& name) { std::cout << "Name: " << name << std::endl; return name; } namespace py = pybind11; PYBIND11_MODULE(example1, m) { m.doc() = "The general function"; m.def("age", &age); m.def("Name", &Name); }
三、pybind11基本用法
1、pybind11封装c++代码
pybind11封装c++代码用法相当简单,所有的封装代码都需要在 PYBIND11_MODULE 函数里面,具体定义如下
PYBIND11_MODULE( 模块名, 模块实例对象 ){ m.doc() = "pybind11 example"; //可选,说明这个模块是做什么的 //封装的具体操作。这些操作包括普通的函数的封装,类的访问等下面用不同例子来说明问题 }
2、调用普通函数
pybind11的模块实例对象提供了 def()函数,用来封装普通的函数,具体的用法为
def( "给python调用方法名", &实际操作的函数, "函数功能说明" ). //其中函数功能说明为可选
下面给一个简单的例子
#include <pybind11/pybind11.h> int add( int i, int j ){ return i+j; } PYBIND11_MODULE( py2cpp, m ){ m.doc() = "pybind11 example"; m.def("add", &add, "add two number" ); }
3、调用类函数的成员函数
pybind11提供了访问成员函数的能力。和访问普通的函数不同,访问成员函数之前先要生成一个对象实例,因此,对任意的一个类而言,都需要有两步,第一步是包装一个实例构造方法,另外一个是成员函数的范围方式
pybind11::class_<命名空间::类名>(m, "在python中构造这个类的方法名" ) .def(pybind11<>::init()) //构造器,对应的是c++类的构造函数,如果没有这个构造函数,或者参数对不是会调用失败 .def( "python中函数名", &命名空间::类名::函数名 );
下面举几个例子,调用命名空间外的类
#include <pybind11/pybind11.h> class Hello { public: Hello(){} void say( const std::string s ){ std::cout << s << std::endl; } }; PYBIND11_MODULE( py2cpp, m ){ m.doc() = "pybind11 example"; pybind11::class_<Hello>(m, "Hello" ) .def(pybind11::init()) .def( "say", &Hello::say ); } //python 调用方式 //1, 先通过构造器来构建实例,方法为 模块名.构造器名 //2,调用对应的方法, 模块名.方法名 //例如本例子需要如下调用 // c=py2cpp.Hello() // c.say()
调用命名空间中的对象
#include <pybind11/pybind11.h> namespace NS{ class World{ public: World(){} void say( const std::string s ){ std::cout << s << std::endl; } }; PYBIND11_MODULE( py2cpp, m ){ m.doc() = "pybind11 example"; pybind11::class_<NS::World>(m, "World") .def(pybind11::init()) .def("say", &NS::World::say); } //本例子需要如下调用 // c=py2cpp.World() // c.say()
4、带参数的构造函数
#include <iostream> #include <pybind11/pybind11.h> class Test{ public: Test( int i, int j ) :mI(i),mJ(j){ } void Print(){ std::cout <<"i= " << mI <<" j= " <<mJ << std::endl; } private: int mI; int mJ; }; PYBIND11_MODULE( py2cpp, m ){ m.doc() = "pybind11 example"; pybind11::class_<Test>(m, "Test" ) .def( pybind11::init< int , int >() ) //构造器的模版参数列表中需要按照构造函数的参数类型填入才能调用对应的参数 .def( "print", &Test::Print ); } //本例子需要如下调用 // c=py2cpp.Test(1,2) // c.print()
5、访问struct/class的公有非成员
对于公有非成员变量的访问,pybind11提供了def_readwrite()方法来支持。具体定义如下
def_readwrite("在python中访问的变量名", &要访问的变量 ); def_property("name", &Test::name) //当name为私有变量时,访问私有变量 //py::dynamic_attr()为C ++类启用动态属性 py::class_<Test>(m, "Test", py::dynamic_attr()) //封装类 .def(py::init<const std::string&>()) .def_readwrite("name", &Test::name) //访问类中公有变量 // .def_property("name", &Test::name) //当name为私有变量时,访问私有变量 .def("setName", &Test::setName) .def("getName", &Test::getName);
下面给出一个例子
#include <pybind11/pybind11.h> struct ST{ std::string str; uint64_t i; }; PYBIND11_MODULE( py2cpp, m ){ m.doc() = "pybind11 example"; pybind11::class_<ST>(m, "ST") .def( pybind11::init() ) .def_readwrite("str", &ST::str ) .def_readwrite("i", &ST::i ); } //本例子需要如下调用 // c = py2cpp.ST() // c.str="hello" // c.i = 123 // print(c.str) // print(c.i)
enum
enum MarkerType { chessboard, tag }; PYBIND11_MODULE( py2cpp, m ){ m.doc() = "pybind11 example"; py::enum_<MarkerType>(m, "MarkerType") .value("chessboard", MarkerType::chessboard) .value("tag", MarkerType::tag) .export_values(); }
四、结构体传参实例
需求:
- 现有名为 student 的结构体,有5个成员变量 name,Chinese,Mathematics,English和total ,构造函数通过name生成实例,成员函数 setName 可以给实例的name赋值;
- calc 函数接收一个student实例作为参数,通过三门课程的分数计算出总分 total ;
- 将student,calc封装到包含一个student类和一个calc函数的python模块( abctest )中。
实现步骤:
- 在头文件中定义student结构体,并声明calc函数;
- 在C++源文件中实现func.cpp函数;
- 编写pybind11封装函数;
- 用python编写setup脚本;
- 编译生成动态链接库;
- 测试函数功能。
代码实现:
在头文件中定义student结构体,并声明calc函数
//文件名:whjy.h #include <string> using namespace std; struct student{ string name; int Chinese; int Mathematics; int English; int total; student(string n){ this->name = n; } void setName(string stuName){ this->name = stuName; } }; void calc(struct student&);
在C++源文件中实现func.cpp函数
/
/
文件名:func.cpp
#include "whjy.h"
#include <string>
void calc(struct student& tyh){
tyh.total
=
tyh.Chinese
+
tyh.Mathematics
+
tyh.English;
}
//文件名:func_wrapper.cpp #include <pybind11/pybind11.h> #include "whjy.h" namespace py = pybind11; PYBIND11_MODULE(abctest, m){ m.doc() = "simple example"; py::class_<student>(m, "student") .def(py::init<string>()) .def("setName", &student::setName) .def_readonly("name", &student::name) .def_readwrite("Chinese", &student::Chinese) .def_readwrite("Mathematics", &student::Mathematics) .def_readwrite("English", &student::English) .def_readwrite("total", &student::total); m.def("calc", &calc); }
五、函数重载示例
头文件:
#include<iostream> struct Pet{ Pet(const std::string &name_, int age_){ name = name_; age = age_; } void set(int age_){ age = age_; } void set(const std::string &name_){ name = name_; } std::string name; int age; };
pybind11:
#include<iostream> #include <pybind11/pybind11.h> #include "struct_pet.h" namespace py = pybind11; PYBIND11_MODULE(struct_pet, m){ py::class_<Pet>(m, "Pet", py::dynamic_attr() ) .def(py::init<const std::string &, int>() ) .def("set", py::overload_cast<int>(&Pet::set), "set pet's age") .def("set", py::overload_cast<const std::string &>(&Pet::set), "set pet's name") .def("__repr__", [](const Pet &a){return "<struct_pet.Pet name: "+a.name+">";}) .def_readwrite("name", &Pet::name) .def_readwrite("age", &Pet::age); }
调用:
from struct_pet import Pet pet = Pet("tbc", 23) print(pet.name, pet.age ) pet.set(10) print(pet.name, pet.age ) pet.set("dindings") print(pet.name, pet.age )