0、引言

        Python‌在基础开发、数据科学、人工智能、Web框架开发等领域具有广泛的支持工具和开发教程,极大的缩短了产品原型开发周期、降低了开发难度。

        有许多的功能,通过C/C++实现,非常的复杂并且不方便,但是Python可能就是几行代码就搞定了。

        为了避免处处重复造轮子,又希望在原先的C/C++通用轻松简单的实现一些功能,因此探索在C/C++中如何嵌入调用Python的API及应用程序非常有必要。

1、开发环境搭建

①、Python

        本文以当前最新的Python 3.13.3版本为例进行讲解,本教程的Python下载地址如下:

Download Python | Python.orghttps://www.python.org/downloads/

        由于Python的版本也一直在更新,如果找查阅到本教程时,最新版本已不是Python3.13.3,可以滑动找到Python的历史版本进行安装。

        并在安装完成后,建议通过终端命令行查询一下具体的版本信息:python --version

②、MinGW

        简单来说,MinGW是一个编译器,提供了gcc和g++编译工具,可以对C/C++程序代码编译调试。

        在之前的很多博客笔记文章中已经对MinGW进行了非常多的讲解了,如果对MinGW编译器有疑惑的,可以通过博主的如下笔记链接,搭建安装MinGW开发环境。

MinGW编译器任意版本使用配置经验教程-CSDN博客https://blog.csdn.net/weixin_49337111/article/details/140274770?spm=1001.2014.3001.5502

③、CMake

        CMake(Cross-platform Make)是一款开源的跨平台构建系统生成工具,广泛应用于C、C++、Fortran等编程语言的项目构建中。

        在VScode,可以直接安装VScode插件构建工具,即可实现在VScode中使用CMake。

        提醒:在一些环境中,可能无法正常使用CMake插件,需要下载最新的CMake程序软件才能辅助使用。

2、工程代码配置

        在上面的开发环境搭建好之后,当在程序代码中加入Python相关的头文件时,大概率会出现如下情况,找不到相关头文件,并且编译会直接报错。

        上面这个情况也就是说明,并没有和Python建立联系,需要我们自己将Python的路径加入到工程代码的配置中。

        通过python --path命令,即可找到python的实际安装路径

        python实际的文件所在路径,include包含了C/C++中需要使用的xxx.h头文件。

        在libs中提供的是_tkinter.lib、python3.lib、python313.lib

        如果在MinGW中不能直接使用xxx.lib库文件,要进行文件格式转换,对于没有接触过了开发者,可能处理起来较为麻烦,如有需要,可以访问博主在下面这篇文章中提到的方法。

Windows中xxx.dll动态链接库文件转xxx.a静态库文件-CSDN博客https://blog.csdn.net/weixin_49337111/article/details/147171368?sharetype=blogdetail&sharerId=147171368&sharerefer=PC&sharesource=weixin_49337111&spm=1011.2480.3001.8118

        如果觉得不方便,可以采取直接在Visual Studio程序中包含xxx.lib文件,同样可以实现C/C++中嵌入Python程序进行开发。

        在完成上面的操作后,即可创建C/C++工程了,然后配置CMakeLists.txt文件。博主在一系列试错了,最终成功运行的文件如下:

        CMakeLists.txt

cmake_minimum_required(VERSION 3.10.0)
project(convert VERSION 0.1.0 LANGUAGES C CXX)

# 设置Python路径
set(PYTHON_INCLUDE_DIR "C:/Users/Administrator/AppData/Local/Programs/Python/Python313/include")
set(PYTHON_LIBRARY "C:/Users/Administrator/AppData/Local/Programs/Python/Python313/libs/python313.lib")

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

# 包含Python头文件目录
target_include_directories(convert PRIVATE ${PYTHON_INCLUDE_DIR})

# 链接Python库
target_link_libraries(convert PRIVATE ${PYTHON_LIBRARY})

# 在Windows上需要定义PYTHON_LIBRARY宏
if(WIN32)
    target_compile_definitions(convert PRIVATE PYTHON_LIBRARY)
endif()

        提醒:如果在Windows环境中,发现存在MSVC会和当前MinGW编译器混用,建议直接换更新版本的MinGW,经过反复试错,最终得出来的正确解决办法,惨痛的教训!!!

        在编译正常运行后,说明环境搭建完毕,工程配置正常,可以开始C/C++中正常调用Python进行开发了。

3、C/C++中调用Python

        对于在C/C++中如何使用Python的API接口,Python官方已经出来非常详细的教程,有需要的博客朋友可以访问如下链接对该教程参考学习。

        Python的C/C++外部扩展官方教程:

1. Embedding Python in Another Application — Python 3.13.3 documentationhttps://docs.python.org/3.13/extending/embedding.html

①、常用API接口

(1)初始化 Python 解释器

//用于初始化 Python 解释器,它会设置 Python 运行所需的环境,加载内置模块等。
void Py_Initialize(void);

(2)终止 Python 解释器

//程序结束时,要调用此函数来终止解释器,释放相关资源。
void Py_Finalize(void);

(3)命令行参数处理

//将 C/C++ 程序的命令行参数传递给 Python 解释器。
void PySys_SetArgv(int argc, char **argv);

(4)执行Python简单语句

//使用此函数接口可以在 C/C++ 代码中执行简单的 Python 语句。
int PyRun_SimpleString(const char *command);

(5)执行 Python 文件

//使用此函数接口,可以在C/C++代码中间接执行 Python 文件
int PyRun_SimpleFile(FILE *fp, const char *filename);

(6)导入 Python 模块

//此函数接口,可以实现导入 Python 模块。
PyObject* PyImport_ImportModule(const char *name);

(7)调用 Python 函数

//用于在 C/C++ 中调用 Python 的可调用对象(函数、方法、类等)。
PyObject* PyObject_CallObject(PyObject *callable, PyObject *args);

②、程序实例

(1)Python语句执行

//    C/C++测试 HTTP 请求和 HTML 解析功能效果如下:
#include <Python.h>
#include <iostream>
#include <string>
#include <stdexcept>

class PythonHttpParser {
public:
    PythonHttpParser() {
        Py_Initialize();
        // 确保 requests 和 bs4 可用
        PyRun_SimpleString(
            "import sys\n"
            "try:\n"
            "    import requests\n"
            "    from bs4 import BeautifulSoup\n"
            "except ImportError as e:\n"
            "    print(f'Error: {e}')\n"
            "    sys.exit(1)\n"
        );
    }
    
    ~PythonHttpParser() {
        Py_Finalize();
    }
    
    std::string fetch_title(const std::string& url) {
        PyObject *pFunc, *pArgs, *pResult;
        
        // 获取 main 模块
        PyObject* main_module = PyImport_AddModule("__main__");
        PyObject* global_dict = PyModule_GetDict(main_module);
        
        // 准备 Python 代码
        const char* code = 
            "def get_page_title(url):\n"
            "    try:\n"
            "        response = requests.get(url)\n"
            "        soup = BeautifulSoup(response.text, 'html.parser')\n"
            "        return soup.title.string if soup.title else 'No title found'\n"
            "    except Exception as e:\n"
            "        return f'Error: {str(e)}'\n";
        
        PyRun_SimpleString(code);
        
        // 获取函数
        pFunc = PyDict_GetItemString(global_dict, "get_page_title");
        if (!pFunc || !PyCallable_Check(pFunc)) {
            throw std::runtime_error("Failed to get Python function");
        }
        
        // 准备参数
        pArgs = PyTuple_New(1);
        PyTuple_SetItem(pArgs, 0, PyUnicode_FromString(url.c_str()));
        
        // 调用函数
        pResult = PyObject_CallObject(pFunc, pArgs);
        if (!pResult) {
            PyErr_Print();
            throw std::runtime_error("Python function call failed");
        }
        
        // 获取结果
        std::string result = PyUnicode_AsUTF8(pResult);
        
        // 清理
        Py_DECREF(pArgs);
        Py_DECREF(pResult);
        
        return result;
    }
};

int main() {
    try {
        PythonHttpParser parser;
        std::string title = parser.fetch_title("https://en.wikipedia.org/wiki/Main_Page");
        std::cout << "Page title: " << title << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}

        C/C++测试 HTTP 请求和 HTML 解析功能效果如下:

(2)Python代码文件调用

#include <Python.h>
#include <cstdio>
#include <cstdlib>

int main(int argc, char* argv[]) {
    // 初始化 Python 解释器
    Py_Initialize();

    // 创建一个 wchar_t** 数组来存储转换后的命令行参数
    wchar_t** wargv = (wchar_t**)malloc(argc * sizeof(wchar_t*));
    if (!wargv) {
        perror("Failed to allocate memory");
        Py_Finalize();
        return 1;
    }

    for (int i = 0; i < argc; ++i) {
        wargv[i] = Py_DecodeLocale(argv[i], nullptr);
        if (!wargv[i]) {
            perror("Failed to decode argument");
            for (int j = 0; j < i; ++j) {
                PyMem_RawFree(wargv[j]);
            }
            free(wargv);
            Py_Finalize();
            return 1;
        }
    }

    // 将转换后的参数传递给 Python
    PySys_SetArgv(argc, wargv);

    // 指定要运行的 Python 脚本文件名
    const char* script_file = "../script.py";

    // 打开 Python 脚本文件
    FILE* fp = fopen(script_file, "r");
    if (!fp) {
        perror("Failed to open file");
        for (int i = 0; i < argc; ++i) {
            PyMem_RawFree(wargv[i]);
        }
        free(wargv);
        Py_Finalize();
        return 1;
    }

    // 运行 Python 脚本
    int result = PyRun_SimpleFile(fp, script_file);
    fclose(fp);

    // 释放分配的内存
    for (int i = 0; i < argc; ++i) {
        PyMem_RawFree(wargv[i]);
    }
    free(wargv);

    // 终止 Python 解释器
    Py_Finalize();

    return result;
}

        script.py

import sys

# 打印接收到的命令行参数
print(f"Received arguments: {sys.argv}")

# 检查是否包含 --help 参数
if "--help" in sys.argv:
    print("Usage: script.py [--input <file>] [--verbose]")
    

        程序执行结果:

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐