python调用c++工程搭建

python调用c++工程搭建

    • 1 安装依赖
    • 2 编写c++代码
    • 3 编译c++代码并安装.so文件
    • 4 编写python代码调用c++接口
    • 一些补充

在学习或者搭建demo时可能会需要使用python调用自己使用c++编写的接口。实际上很多python模块底层都是用c++实现的,比如 cv2、pytorch 等。

那么如何使用python调用自己编写的c++接口呢?原理很简单

  1. 在c++代码中使用特殊技术将c++接口封装成模块并暴露出去;
  2. 将c++代码打包成.so文件;
  3. python加载这个.so文件并导入模块;
  4. 像调用python函数一样调用c++接口;

原理简单,操作起来更简单,下面简单介绍一种搭建python调用c++工程的方法。

1 安装依赖

  • pybind11

pybind11主要用于创建现有C++代码的Python绑定,是一个轻量级的仅头文件库。 源码见:https://github.com/pybind/pybind11
pybind11可以直接使用pip工具安装

pip3 install pybind11

2 编写c++代码

编写c++代码包括两部分:

  • 编写c++接口和业务代码;
  • 使用宏PYBIND11_MODULE注册模块,并向外部暴露;

举个例子,新建cpp文件Sample.cpp

#include 
#include 

int AddNums(int left, int right)
{
    return left + right;
}

// 注册需要暴露给 python 的函数
PYBIND11_MODULE(mathDemo, m)
{
    m.def("AddNums", &AddNums, "Call Add Sums Function");
}

3 编译c++代码并安装.so文件

编译c++代码并生成.so文件的方式有很多种,可以使用 gcc/g++ 命令,也可以使用 cmake 工具。本文使用的是python的包管理工具集setuptools,在工程根目录下新建脚本setup.py

from setuptools import setup, Extension

mathDemo = Extension(
    "mathDemo",
    sources=["Sample.cpp"],
    include_dirs=["/opt/homebrew/lib/python3.10/site-packages/pybind11/include"],    # Note: 替换成 pybind11 头文件实际安装的位置
    language="c++",
    extra_compile_args=["-std=c++11", "-g"],
)

setup(
    name='mathDemo',
    version='0.1',
    ext_modules=[mathDemo],
    options={"install": {"install_lib": "lib"}},    # Note: 可选, 没指定 options["install"] 的话会安装到系统路径下,不妨碍使用
    install_requires=['pybind11>=2.6'],
)

写好setup.py后执行以下命令即可编译c++代码、生成so并自动安装到指定路径

python3 setup.py install

注:setup.py中的 options 是可选项,options[“install”]用于指定.so文件的安装位置。默认安装到系统路径下(可以使用pip3 freeze命令查到)

4 编写python代码调用c++接口

调用c++接口与调用python模块的方法是一样的,编写python代码调用c++代码即可

import sys
sys.path.append("lib/mathDemo-0.1-py3.10-macosx-12-arm64.egg")    # Note: 可选,如果选择安装在系统路径下,这里不需要指定 .so 路径
import mathDemo

value = mathDemo.AddNums(1, 3)    # 调用 c++ 的 AddNums 接口
print(f"sum: {value}")

一些补充

上面的例子很简单,入参和返回值都是基本数据类型,实际上pybind11支持更复杂的数据类型,例如常见的std容器std::mapstd::vectorstd::set以及字符串std::string和自定义结构体。

  • std::string 可以直接与python的str转换
  • std::vectorstd::map可以直接与python的内置list和dict转换
  • 自定义结构体暂时没有研究
  • 引用类型可以直接使用

举个例子
c++代码

#include 
#include 
#include 
#include 
#include 

std::vector<std::map<std::string, int>> MovePoints(std::vector<const std::map<std::string, int>>& pots, std::map<std::string, int> vect)
{
    std::vector<std::map<std::string, int>> resPots(pots.size());
    for (int index = 0; index < pots.size(); index++)
    {
        resPots[index]["x"] = pots[index].at("x") + offset.at("x");
        resPots[index]["y"] = pots[index].at("y") + offset.at("y");
    }
    return resPots;
}

// 注册 MovePoints 
PYBIND11_MODULE(test, m)
{
    m.def("MovePoints", &MovePoints, "Call Move Points Function");
}

python代码

pots = [{"x": 1, "y": 4},
        {"x": 2, "y": 5},
        {"x": 3, "y": 6},
        {"x": 4, "y": 7},
        {"x": 5, "y": 8}]
offset = {"x": -1, "y": 1}
resPots = mathDemo.MovePoints(pots, offset)
print(f"{resPots}")

你可能感兴趣的:(工程,python,c++,开发语言)