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 <pybind11/pybind11.h>
#include <pybind11/stl.h>

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转换
  • 支持自定义结构体
  • 引用类型可以直接使用
- 使用std容器传参

举个例子
c++代码

#include <string>
#include <vector>
#include <map>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

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(demo, 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 = demo.MovePoints(pots, offset)
print(f"{resPots}")
- 使用自定义类型传参

举个例子
c++代码

#include <pybind11/pybind11.h>

class MyClass {
public:
    MyClass(int value) : _value(value), type(0) {}
    int getValue() const { return _value; }

    int type;
private:
    int _value;
};

// 使用pybind11将MyClass绑定到Python
PYBIND11_MODULE(demo, m) {
    pybind11::class_<MyClass>(m, "MyClass")
        .def(pybind11::init<int>())                // 绑定构造函数
        .def_readwrite("type", &MyClass::type);    // 绑定成员变量
        .def("getValue", &MyClass::getValue);      // 绑定成员函数
}

python代码

obj = demo.MyClass(42)
print(obj.type)
print(obj.getValue())
  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用Python ctypes库来调用Python项目,其中可以使用ctypes.pythonapi.Py_Initialize() 函数来初始化Python,以确保Python中的变量一直存在直到C主进程结束。 ### 回答2: 要保证在C调用Python项目时,Python中的变量一直存在直到C主进程结束,可以使用Python的全局变量和C的回调函数来实现。 首先,在Python项目中,将需要保持存在的变量定义为全局变量。通过将变量定义在最外层的作用域,使其在整个项目中都可访问。 然后,使用C的回调函数来获取Python项目中的全局变量的值。在C中,通过调用Python的C API,可以获取指定的全局变量的值,并在C主进程中进行处理。 在C主进程开始时,调用Python项目的初始化函数,使Python的解释器和项目环境准备就绪。 在C中调用Python项目的函数时,可以将需要传递给Python的变量作为参数传递给Python函数。在Python函数中,对这些变量进行处理,并将结果保存在全局变量中。 在C的主进程中,可以定期或根据需要调用回调函数来获取全局变量的值,并进行后续处理。这样可以保证全局变量的存在性,并在C主进程中使用这些变量。 当C主进程结束时,调用Python项目的清理函数,释放Python解释器和项目使用的资源。 通过以上步骤,可以保证在C调用Python项目时,Python中的变量一直存在直到C主进程结束。 ### 回答3: 要实现在C中调用Python项目并且保持Python中的变量一直存在,直到C主进程结束,需要使用Python的C API以及合适的嵌入式Python环境。 首先,我们需要搭建一个Python环境。Python提供了C API,用于在C代码中嵌入Python。可以使用Py_Initialize()函数来初始化Python解释器,并使用PyRun_SimpleString()函数来执行Python代码。 在C代码中,可以通过PyRun_SimpleString()函数执行Python代码。为了确保Python中的变量一直存在,可以将变量定义为全局变量,并在C代码中调用时对其进行访问和修改。 示例代码如下: ```c #include <Python.h> int main() { // 初始化Python解释器 Py_Initialize(); // 设置Python模块路径(如果需要) PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append('/path/to/python/project')"); // 执行Python项目中的代码 PyRun_SimpleString("from my_module import my_variable"); // 访问和修改Python变量 PyObject* pDict = PyDict_New(); PyObject* pModule = PyImport_AddModule("__main__"); pDict = PyModule_GetDict(pModule); PyObject* pVariable = PyDict_GetItemString(pDict, "my_variable"); // 对Python变量进行操作 // ... // 释放Python解释器资源 Py_Finalize(); return 0; } ``` 以上代码中,首先初始化Python解释器,并设置Python项目的模块路径。然后执行Python项目中的代码,这里使用了`from my_module import my_variable`来导入Python模块并定义了一个名为`my_variable`的变量。 在访问和修改Python变量时,我们使用PyDict_New()创建一个空的Python字典对象,并通过PyModule_GetDict()函数获取当前Python主模块的字典对象。然后通过PyDict_GetItemString()函数在字典中查找指定的变量,并进行操作。 最后,释放Python解释器资源,结束程序。 通过以上的嵌入式Python环境,可以在C代码中调用Python项目,并保持Python中的变量一直存在直到C主进程结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值