cpp混合python编程

项目结构

.
├── CMakeLists.txt
├── main.cpp
├── scripts
│   ├── __pycache__
│   └── test.py
└── vendor
    └── python_env

注意点:不要把 venv 当作 PYTHONHOME,只用作依赖库存放,并在运行时将它加入 sys.path

构建 python 虚拟环境

python -m venv python_env
source python_env/bin/active
pip install pybind11

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(CppPythonEmbedded)

set(CMAKE_CXX_STANDARD 17)

# 设置 Python 路径为本地虚拟环境
set(Python3_ROOT_DIR "${CMAKE_SOURCE_DIR}/vendor/python_env")
set(Python3_EXECUTABLE "${Python3_ROOT_DIR}/bin/python3")

# 查找本地 Python
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)

# 查找本地 pybind11
execute_process(
    COMMAND ${Python3_EXECUTABLE} -m pybind11 --includes
    OUTPUT_VARIABLE PYBIND11_INCLUDES
    OUTPUT_STRIP_TRAILING_WHITESPACE
)

execute_process(
    COMMAND ${Python3_EXECUTABLE} -m pybind11 --cmakedir
    OUTPUT_VARIABLE PYBIND11_DIR
    OUTPUT_STRIP_TRAILING_WHITESPACE
)

list(APPEND CMAKE_PREFIX_PATH ${PYBIND11_DIR})

find_package(pybind11 REQUIRED)

# 添加可执行文件
add_executable(main main.cpp)

# 链接 pybind11 嵌入库
target_link_libraries(main PRIVATE pybind11::embed)

# 包含 Python 头文件
target_include_directories(main PRIVATE ${Python3_INCLUDE_DIRS})
target_link_libraries(main PRIVATE ${Python3_LIBRARIES})

简单脚本执行

main.cpp

#include <iostream>
#include <pybind11/embed.h>
namespace py = pybind11;

int main() {
    py::scoped_interpreter guard{};

    py::module sys = py::module::import("sys");
    // 添加 venv site-packages
    sys.attr("path").attr("insert")(0, "./vendor/python_env/lib/python3.12/site-packages");
    // 添加脚本目录
    sys.attr("path").attr("insert")(0, "./scripts");

    py::module example = py::module::import("test");
    auto res_add = example.attr("add")(1, 1).cast<int>();
    auto res_mult = example.attr("multiply")(2, 3).cast<int>();
    std::cout << "add:  " << res_add << std::endl;
    std::cout << "mult: " << res_mult << std::endl;
}

test.py

def add(a, b):
    return a + b

def multiply(a, b):
    return a * b

构建运行

./build/main
add:  2
mult: 6

引入依赖

比如使用 matplotlib

使用依赖:

pip install matplotlib

main.cpp

#include <pybind11/embed.h>
#include <pybind11/stl.h>
#include <iostream>

namespace py = pybind11;

int main() {
    // -----------------------------
    // 设置 Python 环境(跨平台)
    // -----------------------------
#ifdef _WIN32
    // Windows 下可用 embeddable Python 或系统 Python
    // _putenv_s("PYTHONHOME", ".\\vendor\\python_env");  // 不建议直接指向 venv
#else
    // Linux/macOS
    // setenv("PYTHONHOME", "/usr", 1);  // 可选,系统 Python 默认
#endif

    py::scoped_interpreter guard{};

    py::module sys = py::module::import("sys");
    // 添加 scripts
    sys.attr("path").attr("insert")(0, "./scripts");
    // 添加虚拟环境 site-packages
#ifdef _WIN32
    sys.attr("path").attr("insert")(0, ".\\vendor\\python_env\\Lib\\site-packages");
#else
    sys.attr("path").attr("insert")(0, "./vendor/python_env/lib/python3.12/site-packages");
#endif

    py::module plot_example = py::module::import("plot_test");
    std::string filename = plot_example.attr("plot_sin")().cast<std::string>();

    std::cout << "Saved plot to " << filename << std::endl;
}

plot_test.py

import matplotlib.pyplot as plt
import numpy as np

def plot_sin():
    x = np.linspace(0, 2*np.pi, 100)
    y = np.sin(x)

    plt.plot(x, y)
    plt.title("Sine Wave")
    plt.xlabel("x")
    plt.ylabel("sin(x)")
    plt.savefig("sin_wave.png")  # 保存图像
    return "sin_wave.png"

sin wave


comment: