• PyBind11踩坑笔记


    PyBind11可以让C++快速和Python进行绑定,有如下情况可以使用

    1、我需要使用c++给python写库(打包pyd)

    2、我的c++程序需要内嵌python(打包exe)

    首先需要明确依赖项,以下是CMake脚本

    1. include_directories("C:/Users/yanyan/AppData/Local/Programs/Python/Python310/include")
    2. include_directories("C:/Users/yanyan/AppData/Local/Programs/Python/Python310/Lib/site-packages/pybind11/include")
    3. link_directories("C:/Users/yanyan/AppData/Local/Programs/Python/Python310/libs")
    4. LINK_LIBRARIES(python3 python310)

    解释一下,pybind可以通过pip来安装,并且依赖python的头文件

    另外还需要手动把相关的dll放到exe目录python310.dll python3.dll

    如果你想把dll文件放到Bin目录里或其他目录,需要设置系统的环境变量PATH

    导入相关头文件

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. namespace py = pybind11;

    不一定都用,但是都导入总没错的,之后就可以愉快的用c++对python进行绑定了

    运行py脚本

    1. //启动解释器并保持其活动状态
    2. py::module_ sys = py::module_::import("sys");
    3. sys.attr("path")
    4. .attr("append")("./Content/");
    5. try {
    6. mengyaengine = py::module_::import("MengYaEngine");
    7. }
    8. catch (py::error_already_set& e) {
    9. py::print(e.type());
    10. py::print(e.what());
    11. return ;
    12. }

    首先sys可以把环境变量添加我们的目录,方便后面找到我们的py脚本

    然后导入我们自己的py模块文件,如果出错就打印错误。

    绑定C++类

    1. PYBIND11_EMBEDDED_MODULE(MengYa, m) {
    2. py::class_(m, "MRect")
    3. .def(py::init<int, int, int, int>());
    4. m.def("addPaintWidget", &addPaintWidget);

    这是一个很大的宏,我在CLion可以直接看到展开后的样子,有点多我就不发出来了,想了解的可以自己看看。

    MengYa是我的import模块的名字,可以自定义

    m表示模块,可以绑定普通函数(使用def)

    py::class_模板里填需要导出的c++类
    py::init<>如果你的类构造函数没东西就不需要加这四个int,我这个类初始化四个整数才要加

    绑定带虚函数的C++类

    有时候我们的c++类需要子类重写,所以要加virtual关键词

    这里绑定的时候也跟上面差不多

    1. py::class_(m, "MWidget")
    2. .def(py::init<>())
    3. .def("setColor", &MWidget::setColor)
    4. .def("setSize", &MWidget::setSize)
    5. .def("setPos", &MWidget::setPos)
    6. .def("paint", &MWidget::paint)

    可以看到我们的类是MWidget,但旁边多了个新写的PyMWidget类

    原来是pybind11绑定带有虚函数的类时只能自己写个工具类,如下

    1. class PyMWidget : public MWidget {
    2. public:
    3. /* Inherit the constructors */
    4. using MWidget::MWidget;
    5. /* Trampoline (need one for each virtual function) */
    6. void paint() override {
    7. PYBIND11_OVERRIDE(
    8. void, /* Return type */
    9. MWidget, /* Parent class */
    10. paint, /* Name of function in C++ (must match Python name) */
    11. );
    12. }
    13. void mousePressEvent(py::args args) override {
    14. PYBIND11_OVERRIDE(
    15. void,
    16. MWidget,
    17. mousePressEvent,
    18. args
    19. );
    20. }

    稍微有点麻烦,不过熟了以后就好了,具体可以看pybind11文档,

    override关键词表示重写这个成员函数

    如果你的C++类是子类也需要重新抄一遍上述的工具类

    1. py::class_(m, "MButton")
    2. .def(py::init<>())
    3. .def("setButtonIamge", &MButton::setButtonIamge)
    4. .def("setButtonImageColor", &MButton::setButtonImageColor)
    5. .def("setButtonNormalColor", &MButton::setButtonNormalColor)
    6. .def("setButtonFocusColor", &MButton::setButtonFocusColor)
    7. .def("setButtonPressedColor", &MButton::setButtonPressedColor);

    重点看尖括号里面的三个类,MLabel是父类MButton是子类,

    PyMButton是继承自MButton的工具类

    绑定C++模板类

    1. py::class_>(m, "MLayout")
    2. .def(py::init<>())
    3. .def("addWidget", &MLayout::addWidget);
    4. py::class_>(m, "MHLayout")
    5. .def(py::init<>());

    注意看MLayout,其实模板类也跟上述差不多,详情也看pybind11文档

    模板函数也差不多

    1. py::class_(m, "Signal")
    2. .def(py::init<>())
    3. .def("Bind", &Signal::Bind)
    4. .def("emit", &Signal::emit);

    绑定类成员变量

    .def_readwrite("signal_click", &MWidget::signal_click)

    也跟def差不多

    绑定的函数如果参数或返回值使用到了其他类型指针

    1、可以也把参数的类型绑定上,可以只绑用到的成员函数就行

    2、如果出现错误可以先把参数变成void*再手动进行类型转换

    补充:pybind11会把常见的类型自动转换,c++类到python相当于把原始指针包装一层

    python函数指针给c++调用

    python的函数指针类型是py::function

    这是一个类,它的实例可以直接当函数来用,因为operator()()把实例的括号改变了意义。

     比如

    1. py::function a;
    2. a();

    当然我们上面这个a函数啥都没有,实际上需要python那边传过来的才行,我们自己在c++定义的这个没卵用。上面只是说了a为啥可以当函数来用,但它不是函数指针。

    补充std::function可以了解一下,这里我们用不上

    py::cpp_function类是py::function的子类

    可以在c++里面定义一个这种函数指针,比如下面我的拉姆达表达式

    1. py::cpp_function func([this,widget](){this->paintlist.erase(remove(this->paintlist.begin(), this->paintlist.end(), widget), this->paintlist.end());});
    2. widget->signal_close.Bind((py::function)func);

    可以看到还需要转换一下,当然这是因为的Bind模板函数的类型就是py::function

    这个地方卡了我很久

    另外这个类的参数是py::args args,我们可以给它传递python列表,一个参数顶n个参数

    所以调用函数指针的话就方便了,不需要研究c++的可变参数,那玩意有点难搞

    1. MWidget::~MWidget()
    2. {
    3. py::list e;
    4. e.append(this);
    5. signal_close.emit(e);
    6. std::cout << this << "控件被删除" << std::endl;
    7. }

    上面这个e就当做py::args类型传递进参数里了,我们的this是c++指针,但是由于之前我们以及绑定了,所以直接这样append不会有问题,pybind11还是非常智能的

  • 相关阅读:
    Maven下载+配置+idea配置
    Ajax--》请求操作以及跨域相关讲解
    一文读懂 MongoDB 和 MySQL 的差异
    手写Spring-第十一章-用动态代理实现AOP核心功能
    黑魂向project制作学习二:Camera Handler(主摄像头跟随人物且旋转)
    mysql学习笔记
    【推理引擎】从源码看ONNXRuntime的执行流程
    pyqt5移动鼠标时显示鼠标坐标
    车内静谧性超越埃尔法?走进腾势D9身价上亿的NVH实验室
    MariaDB MaxScale实现mysql8读写分离
  • 原文地址:https://blog.csdn.net/u012863565/article/details/126091062