Python 调用 C++

欢迎访问我的博客首页


1. pybind11


  使用 pybind11 创建一个 python 扩展模块,并使用 python 测试。

1.1 使用 cmake 编译


  1. 配置 pybind11

  下载 pybind11,确保文件夹名称为 pybind11,无需编译和安装。

  1. 新建 main.cpp
#include <pybind11/pybind11.h>
#include <string>

namespace py = pybind11;

class Person {
public:
    Person() {}
    Person(const std::string &_name) { name = _name; }
    void setName(const std::string &_name) { name = _name; }
    std::string &getName() { return name; }

private:
    std::string name;
};

int subtract(int a, int b) { return a - b; }

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example module";
    m.def("add", [](int a, int b) { return a + b; });
    m.def("subtract", &subtract);
    py::class_<Person>(m, "Person")
        .def(py::init())
        .def(py::init<const std::string &>())
        .def("setName", &Person::setName)
        .def("getName", &Person::getName);
}

  这个例子实现了两个函数和一个类。第一个函数使用 lambda 表达式实现加法;第二个函数实现减法;类有无参构造函数和有参构造函数。

  1. 编辑 cmake 配置文件

  project 后面的括号中是项目名称,使用 cmake 生成的 vs 项目名称也是由这个名称决定的。

cmake_minimum_required(VERSION 3.2)
project(test)

include_directories(pybind11/include)	# 指定 pybind11/pybind11.h 所在文件夹。
add_subdirectory(pybind11)				# 下载得到的 pybind11。
pybind11_add_module(example main.cpp)	# 使用 pybind11 的命令生成 python 扩展模块。
  1. 编译

  新建文件夹 build,在其中依次执行下面的命令,生成 example.cp37-win_amd64.pyd。生成的扩展模块名字的某些字段会因不同的 python 版本和平台而有所差异。

cmake -G "MinGW Makefiles" ..
make
  1. 测试

  在 build 文件夹中创建 main.py,运行即可看到结果。

import example

if __name__ == '__main__':
	print(example.add(1, 2))
	print(example.subtract(1, 2))
	person1 = example.Person()
	person1.setName("XiaoMing")
	print(person1.getName())
	person2 = example.Person("LiHua")
	print(person2.getName())

  pybind11 是一个 C++ 库,它能把用 C++ 语言写的代码编译成 python 扩展模块。注意两个命令 PYBIND11_MODULE 与 pybind11_add_module 指定的扩展模块名必须一致。

1.2 使用 visual studio 编译


  1. 创建 vs 项目
    创建空项目,项目名称为 example,解决方案名称为 exampleproject。

  2. 准备第三方库
    2.1 下载 pybind11,得到文件夹 pybind11。
    2.2 假如使用的 python 版本是 3.8。创建文件夹 python38,拷入 python3.8 安装位置中的 include 和 libs 文件夹。
    2.3 在文件夹 exampleproject/example 中新建文件夹 thirdpart,拷入上面得到的两个文件夹 pybind11 和 python38。

  3. 配置 vs 属性
    3.1 在常规选项卡上,设置目标文件扩展名为 .pyd,设置配置类型为动态库(.dll)。有的版本的 vs,目标文件扩展名在高级选项卡上。
    3.2 设置包含目录为 thridpart\pybind11\include 和 thridpart\python38\include。设置库目录为 thridpart\python38\libs。设置附加依赖项为 python3.lib 和 python38.lib。

  4. 添加 main.cpp 生成 example.pyd。

  5. 测试
    因为编译扩展模块时使用了 python 环境,所以测试也要在该 python 环境下进行。

1.3 使用 cmake 生成 vs 项目


  上面的两种方法中,使用 cmake 编译不需要配置 python 环境中的头文件目录和库目录,pybind11 会自动查找;使用 visual studio 编译需要配置 python 环境中的头文件目录和库目录,且生成的扩展库名字只包含项目名。原因是,使用 cmake 时,我们用的编译命令是 pybind11 的 pybind11_add_module;使用 vs 时,我们没有用 pybind11 的命令,而是把它当作 dll 编译。

  一些第三方库很容易在 vs 上配置,却不一定能在 cmake 上配置,因此使用 vs 编译 python 扩展模块是很有必要的。这时,我们可以先用 cmake 编译上面那样简单的 pybind11 例程,然后生成 vs 项目,接着在 vs 项目上配置第三方库,再把例程修改为需要的代码,最后就可以生成名字类似 example.cp37-win_amd64.pyd 的 python 扩展模块了。

2. ctypes


  使用 vs 新建空项目,配置类型设置为动态库(.dll),然后使用下面的代码生成 dll 文件。

#define EXPORT extern "C" __declspec(dllexport)

EXPORT int add(int a, int b) { return a + b; }

3. 总结


  使用 pybind11 生成 python 控制模块时,可以不需要 cmake,仅使用 vs 和 pybind11 库。pybind11 库由头文件组成,无须编译安装。但是这种方法生成的 python 扩展模块只能在对应的 python 版本下使用。

  使用 ctypes 时,编译的 dll 和 python 版本无关,可以在任意 python 版本下使用。但 ctypes 只能调用函数,不能创建类,而且 python 调用 dll 较复杂,比如需要使用 restype 和 argtypes 指出 python 所调 C++ 函数的返回值类型和参数类型。

  我使用这两种方式调用一个包含类似下面函数的第三方库时,该函数得到的返回值和实参是 0 或 ±inf,其它不涉及浮点数的函数都正常。用自己编译的一个包含这种函数的库,又完全正常,目前还不知道什么原因。解决办法是不使用堆内存,即,不使用 new 创建对象。

float Example::fac(float& a);

4. 参考


  1. pybind11 的安装与配置使用,CSDN,2022。
  2. pybind11在visual studio中的配置,CSDN,2022。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值