Windows 下用 C++ 调用 Python

文章目录

  • Part.I Introduction
    • Chap.I Information
    • Chap.II 预备知识
  • Part.II 语法
    • Chap.I PyRun_SimpleString
    • Chap.II C++ / Python 变量之间的相互转换
  • Part.III 实例
    • Chap.I 文件内容
    • Chap.II 基于 Visual Studio IDE
    • Chap.III 基于 cmake
    • Chap.IV 运行结果
  • Part.IV 可能出现的问题
    • Chap.I 无法打开 python311_d.lib
    • Chap.II 导入模块报错
    • Chap.I PyEval_CallObject 调用报错
  • Reference

Part.I Introduction

在这里插入图片描述

本文主要介绍一下如何使用 C++ 调用 Python。包括运行 python 脚本;C++ 和 Python 之间参数的相互转换和传递。

Chap.I Information

下面是笔者python 相关的目录,可参考

  • python.exe 所在目录:A:\Programs\Python\Python11_4
  • python311.lib 所在目录:A:\Programs\Python\Python11_4\libs
  • include 所在目录:A:\Programs\Python\Python11_4\include

Chap.II 预备知识

Part.II 语法

Chap.I PyRun_SimpleString

Python 库函数 PyRun_SimpleString 可以执行字符串形式的Python代码。不过在使用 PyRun_SimpleString 之前需要先初始化(Py_Initialize()),执行完之后需要释放资源Py_Finalize(),示例:

Py_Initialize();
PyRun_SimpleString("print('Hello!')");
Py_Finalize();

Chap.II C++ / Python 变量之间的相互转换

有待总结和补充,可看下面的实例。

Part.III 实例

Chap.I 文件内容

所用到的几个文件组织结构如下

test
│  CMakeLists.txt
│  hello.py
│  main.cpp
└─build

hello.py中有两个小函数,内容如下:

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

def get_name(first):
    return "your name is {} alice".format(first)

main.cpp文件内容如下:

#include <iostream>
#include <Python.h>

using namespace std;

const int kError = -1;
const int kSuccess = 0;


/**
 * @brief  Initializes the python interpreter
 */
int pythonInit() {
    Py_Initialize();
    int ret = Py_IsInitialized();
    if (ret == 0) {
        cout << "Py_Initialize error" << endl;
        return kError;
    }
    return kSuccess;
}


/**
 * @brief  Release resources requested by Python
 */
void pythonCleanup() {
    Py_Finalize();
}


/**
 * @brief  Import module ${name} in ${pyDir}
 * @param[in] pyDir     The path of the python script
 * @param[in] name      The name of the python script
 * @return              the module object
 */
PyObject *pythonImportModule(const char *pyDir, const char *name) {
    // 引入当前路径,否则下面模块不能正常导入
    char tempPath[256] = {};
    sprintf(tempPath, "sys.path.append('%s')", pyDir);
    PyRun_SimpleString("import sys");
    //PyRun_SimpleString("sys.path.append('./')");
    PyRun_SimpleString(tempPath);
    PyRun_SimpleString("print('curr sys.path', sys.path)");

    // import ${name}
    PyObject *module = PyImport_ImportModule(name);
    if (module == nullptr) {
        PyErr_Print();
        cout << "PyImport_ImportModule '" << name << "' not found" << endl;
        return nullptr;
    }

    return module;
}


/**
 * @brief  Call 'add' function in the python script
 * @param[in] module    The name of module
 * @param[in] a         The value of a
 * @param[in] b         The value of b
 * @return              a + b
 */
int callPythonAdd(PyObject *module, int a, int b) {
    //获取模块字典属性
    PyObject *pDict = PyModule_GetDict(module);
    if (pDict == nullptr) {
        PyErr_Print();
        std::cout << "PyModule_GetDict error" << std::endl;
        return kError;
    }

    //直接获取模块中的函数
    PyObject *addFunc = PyDict_GetItemString(pDict, "add");
    if (addFunc == nullptr) {
        std::cout << "PyDict_GetItemString 'add' not found" << std::endl;
        return kError;
    }

    // 构造python 函数入参, 接收2
    // see: https://docs.python.org/zh-cn/3.7/c-api/arg.html?highlight=pyarg_parse#c.PyArg_Parse
    PyObject *pArg = Py_BuildValue("(i,i)", a, b);

    //调用函数,并得到 python 类型的返回值
    PyObject *result = PyEval_CallObject(addFunc, pArg);

    int ret = 0;
    //将python类型的返回值转换为c/c++类型
    PyArg_Parse(result, "i", &ret);
    return ret;
}


/**
 * @brief  Call 'get_name' function in the python script
 * @param[in] module    The name of module
 * @param[in] firstName The firstname
 * @param[in] outName   The fullname
 * @return              success or not
 */
int callPythonGetName(PyObject *module, std::string firstName, std::string &outName) {
    //获取模块字典属性
    PyObject *pDict = PyModule_GetDict(module);
    if (pDict == nullptr) {
        PyErr_Print();
        std::cout << "PyModule_GetDict error" << std::endl;
        return kError;
    }

    //直接获取模块中的函数
    PyObject *addFunc = PyDict_GetItemString(pDict, "get_name");
    if (addFunc == nullptr) {
        std::cout << "PyDict_GetItemString 'add' not found" << std::endl;
        return kError;
    }

    // 构造python 函数入参, 接收2
    // see: https://docs.python.org/zh-cn/3.7/c-api/arg.html?highlight=pyarg_parse#c.PyArg_Parse
    PyObject *pArg = Py_BuildValue("(s)", firstName.c_str());

    //调用函数,并得到python类型的返回值
    PyObject *result = PyEval_CallObject(addFunc, pArg);

    char *name = nullptr;
    //将python类型的返回值转换为c/c++类型
    PyArg_Parse(result, "s", &name);

    char tempStr[256] = {};
    int strLen = strlen(name);
    if (strLen > 256) {
        return kError;
    }
    strcpy(tempStr, name);
    outName = tempStr;
    return kSuccess;
}


int main() {
    pythonInit();

    //直接运行python代码
    PyRun_SimpleString("print('---------- Hello Python form C/C++ ----------')");

    PyObject *helloModule = pythonImportModule("../", "hello");    // 这里最好还是给绝对路径吧
    if (helloModule == nullptr) {
        return -1;
    }

    // call python add function
    int result = callPythonAdd(helloModule, 1, 3);
    cout << "1 + 3 = " << result << endl;

    // call python get_name function
    std::string fullName;
    callPythonGetName(helloModule, "summer", fullName);
    cout << fullName << endl;

    pythonCleanup();
}

CMakeLists.txt 文件内容等会儿说;build 是一个空文件,生成的二进制文件等放这里面。

Chap.II 基于 Visual Studio IDE

1、首先新建一个工程,将main.cpp添加进去,然后将hello.py放在和main.cpp一样的路径下。

2、将 IDE 上方『解决方案平台』设置为x64,最好将『解决方案配置』设置为Release(debug 需要*_d.lib

3、将include添加到 C/C++ 附加包含目录中:项目右键→属性→C/C++→附加包含目录→添加 python 的 include

在这里插入图片描述
右键属性→链接器→常规→附加库目录→将 python 的 libs 加进去

在这里插入图片描述
3、将*.lib添加到附加依赖项中:项目右键→属性→链接器→输入→将python311.libpython311_d.lib加进去。

在这里插入图片描述

Chap.III 基于 cmake

cmake 自动搜寻 python

CMakeLists.txt文件内容如下:

cmake_minimum_required( VERSION 3.20 )

project( test )

set( PRJ_INCLUDE_DIRS )
set( PRJ_COMPILE_FEATURES )
set( PRJ_LIBRARIES )

list( APPEND PRJ_COMPILE_FEATURES cxx_std_20 )

find_package(Python3 COMPONENTS Interpreter Development)

message( STATUS "Python3_FOUND = ${Python3_FOUND} " )
message( STATUS "Python3_INCLUDE_DIRS = ${Python3_INCLUDE_DIRS} " )
message( STATUS "Python3_LIBRARIES = ${Python3_LIBRARIES} " )

if( ${Python3_FOUND} )
    #include_directories(${Python3_INCLUDE_DIRS})
else()
    message( FATAL_ERROR "Python3 not found, please install it." )
endif()

list( APPEND PRJ_INCLUDE_DIRS ${Python3_INCLUDE_DIRS} )
list( APPEND PRJ_LIBRARIES ${Python3_LIBRARIES} )

message( STATUS "PRJ_LIBRARIES = ${PRJ_LIBRARIES} " )

add_executable( ${PROJECT_NAME}
    main.cpp
)

target_include_directories( ${PROJECT_NAME}
    PRIVATE 
        ${PRJ_INCLUDE_DIRS}
)

target_link_libraries( ${PROJECT_NAME} 
    PRIVATE 
        ${PRJ_LIBRARIES}
)

target_compile_features( ${PROJECT_NAME} 
    PRIVATE 
        ${PRJ_COMPILE_FEATURES}
)

用 CMake 编译一下
在这里插入图片描述
然后直接跑就可以!


cmake 手动设置 Python 路径

CMakeLists.txt文件内容如下:

cmake_minimum_required( VERSION 3.20 )

project( test )

set( PRJ_INCLUDE_DIRS )
set( PRJ_COMPILE_FEATURES )
set( PRJ_LIBRARIES )

list( APPEND PRJ_COMPILE_FEATURES cxx_std_20 )

set( Python3_INCLUDE_DIRS "A:/Programs/Python/Python11_4/include")
set( Python3_LIBRARIES "A:/Programs/Python/Python11_4/libs/python311.lib"
                       "A:/Programs/Python/Python11_4/libs/python311_d.lib" )


message( STATUS "Python3_INCLUDE_DIRS = ${Python3_INCLUDE_DIRS} " )
message( STATUS "Python3_LIBRARIES = ${Python3_LIBRARIES} " )


list( APPEND PRJ_INCLUDE_DIRS ${Python3_INCLUDE_DIRS} )
list( APPEND PRJ_LIBRARIES ${Python3_LIBRARIES} )

message( STATUS "PRJ_LIBRARIES = ${PRJ_LIBRARIES} " )

add_executable( ${PROJECT_NAME}
    main.cpp
)

target_include_directories( ${PROJECT_NAME}
    PRIVATE 
        ${PRJ_INCLUDE_DIRS}
)

target_link_libraries( ${PROJECT_NAME} 
    PRIVATE 
        ${PRJ_LIBRARIES}
)

target_compile_features( ${PROJECT_NAME} 
    PRIVATE 
        ${PRJ_COMPILE_FEATURES}
)

Chap.IV 运行结果

---------- Hello Python form C/C++ ----------
curr sys.path ['A:\\Programs\\Python\\Python11_4\\python311.zip', 'A:\\Programs\\Python\\Python11_4\\DLLs', 'A:\\Programs\\Python\\Python11_4\\Lib', 'A:\\aWork\\scripts\\test1\\test\\build\\Debug', 'A:\\Programs\\Python\\Python11_4', 'A:\\Programs\\Python\\Python11_4\\Lib\\site-packages', '../']
1 + 3 = 4
your name is summer alice

Part.IV 可能出现的问题

Chap.I 无法打开 python311_d.lib

无法打开 python311_d.lib 的问题:笔者使用的 python 版本是 Python 11.4,它的libs中没有python311_d.lib,只有python311.lib(因为安装的时候没有勾选安装 debug 的 lib),解决方法有三个:

  1. 不使用debug模式运行程序,使用release或其他模式运行 C++ 程序
  2. 找到pyconfig.h文件(一般在py_dir/include文件夹下),注释下面的内容(有点危险)
#ifdef _DEBUG
#       define Py_DEBUG
#endif
  1. 安装 python 的 debug 版本库:安装程序→更改→勾选Download debug binaries (requires VS 2017 or later)

Chap.II 导入模块报错

这种情况下是没有把 python 脚本所在的路径加到sys.path里面,使用sys.path.append(your_path_xx)添加一下就可以了。

Chap.I PyEval_CallObject 调用报错

报错内容:

'PyEval_CallObjectWithKeywords': deprecated in 3.9

PyEval_CallObject替换为PyObject_CallObject就行了。

Reference

  • Linux 系统下通过 cmake 使 C++ 调用 Python

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/291075.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

银河麒麟桌面版开机后网络无法自动链接

下载并上传nm_3.0.1-1kylin77_arm64.deb 包。 下载链接&#xff1a;链接: https://pan.baidu.com/s/1rGPD8qJfjRui6lCC6QjHVw?pwdeeaf 提取码: eeaf 使用管理员命令运行安装sudo dpkg -i nm_3.0.1-1kylin77_arm64.deb 然后运行重启网卡命令sudo systemctl restart NetworkM…

react / antd ProTable - 高级表格 合并行,子表头

ProTable - 高级表格 合并行,以及ProTable的用法 key React.key 确定这个列的唯一值,一般用于 dataIndex 重复的情况 dataIndex React.key | React.key[] 与实体映射的 key,数组会被转化 [a,b] => Entity.a.b valueType ProFieldValueType 数据的渲染方式,我们自带了一部…

《深入理解C++11:C++11新特性解析与应用》笔记六

第六章 提高性能及操作硬件的能力 6.1 常量表达式 6.1.1 运行时常量性与编译时常量性 大多数情况下&#xff0c;const描述的是运行时常量性&#xff0c;也即是运行时数据的不可更改性。但有时候我们需要的却是编译时的常量性&#xff0c;这是const关键字无法保证的。例如&am…

「许战海战略文库」佳隆股份:2亿级别的调味品公司如何应对增长难题

自2002年以来&#xff0c;佳隆食品逐步向集团化方向发展&#xff0c;2010年11月2日在深圳证券交易所成功挂牌上市。 2009年-2022年&#xff0c;公司营收增长并不明显&#xff0c;基本维持在2-3亿之间。尤其是2022年&#xff0c;营收出现亏损的情况&#xff0c;在运营和增长战略…

接口测试基础知识总结

一、HTTP 1、http请求头和响应头包含那些内容&#xff1f; 请求头信息 请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。 2、常用的请求报头如下&#xff1a; Accept&#xff1a;浏览器可接受的MIME类型。 l MIME用于设定某种扩展名的文件用哪种应…

静态S5在项目管理中的应用与案例分享

静态S5作为一种强大的数据分析工具&#xff0c;不仅在数据处理和可视化方面表现出色&#xff0c;还在项目管理中发挥着重要作用。本篇将通过实际案例分享&#xff0c;探讨静态S5在项目管理中的应用与优势。 一、静态S5在项目管理中的应用 项目进度管理&#xff1a;静态S5通过…

计算机网络 综合(习题)

【计算机网络习题】系列文章目录 计算机网络 第一章 绪论(习题) 计算机网络 第二章 计算机网络体系结构(习题) 计算机网络 第三章 应用层(习题) 计算机网络 第四章 运输层(习题) 计算机网络 第五章 网络层(习题) 计算机网络 第六章 数据链路层(习题) 计算机网络 第七章 物…

第三方软件测试机构可提供哪些服务类型?如何收费?

随着高新技术的快速发展&#xff0c;软件企业将测试工作交由第三方软件测试机构进行已经成为了行业趋势&#xff0c;因为企业自身的大多精力都投入在产品开发上&#xff0c;第三方软件测试机构的存在也就极大的提供了便利。 第三方软件测试机构是区别于软件企业与软件需求方的…

书客、孩视宝、明基护眼台灯好不好用?护眼灯测评对比!

现在一些家长对自家孩子的健康也是特别的重视&#xff0c;从小时开始的儿童枕&#xff1b;再到保护眼睛的护眼台灯、OK眼镜&#xff1b;再到青少年时期的生长激素...可以说是穷出不尽&#xff0c;但是关于孩子使用的东西又不能马虎&#xff0c;每次要帮孩子选东西的时候可是一阵…

《代码整洁之道之程序员的职业素养》-专业主义

专业主义有很深的含义&#xff0c;它不但象征着荣誉和骄傲&#xff0c;而且明确意味着责任和义务担当责任&#xff0c;“为了按时交付软件&#xff0c;没测例行程序&#xff0c;测试例行程序需要几个小时&#xff0c;当时必须交付软件&#xff0c;因为故障修复部分都不涉及例行…

在Docker中安装Tomact

目录 前言&#xff1a; 一.安装Tomact 查找指定的tomact版本 下载tomact9.0 查看该镜像是否安装成功 安装成功之后就开始运行镜像了 ps&#xff08;用于列出正在运行的Docker容器&#xff09; ​编辑 测试(虚拟机ip:8080) ​编辑 解决措施 ​编辑 完成以上步骤&…

k8s 1.23.5版本安装ingress1.6.4

1、背景 网上找了好几个ingress 文件&#xff0c;可能是版本没对&#xff0c;ingress都没有安装成功&#xff0c;最后查了相关资料&#xff0c;手动安装了。 下面是版本的匹配列表 github中ingress 地址&#xff1a;https://github.com/kubernetes/ingress-nginx 1.23.5版本支…

【问题解决】web页面html锚点定位后内容被遮挡问题解决【暗锚】

正常的锚点跳转 a标签的href填写目标元素的id即可 <a href"#my_target">to div1</a> <div id"my_target">div1</div> 顶栏被遮挡示例 但是当id所在元素被嵌套多层flex和relative布局之后&#xff0c;跳转后部分内容会被遮挡。 …

百度Apollo:激光雷达检测技术深度解析

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 粉丝福利活动 ✅参与方式&#xff1a;通过连接报名观看课程&#xff0c;即可免费获取精美周边 ⛳️活动链接&#xf…

嵌入式与单片机之间的关系是什么?

今日话题&#xff0c;嵌入式与单片机之间的关系是什么&#xff1f;可以这样理解&#xff1a;嵌入式系统是一个大的范畴&#xff0c;而单片机则是嵌入式系统中的一个重要子类。通常情况下&#xff0c;制造商出厂的通用单片机内并没有预装应用程序&#xff0c;因此无法直接运行。…

如何在Windows 10中扩展分区,这里提供步骤

一些PC制造商提供的电脑硬盘分为多个分区&#xff0c;一个用于Windows操作系统&#xff0c;另一个用于个人文件的空“数据”分区。如果你愿意&#xff0c;可以将这些分区合并为一个分区。 此技巧还可以用于删除恢复分区&#xff0c;从而释放通常用于恢复数据的空间。或者&…

灵芝,到2025年有望达到9.2亿美元

灵芝是一种传统的药食两用菌&#xff0c;其具有丰富的营养成分和医疗价值&#xff0c;因此备受关注。全球市场分析 从全球市场来看&#xff0c;近年来灵芝的市场需求持续增长。据估计&#xff0c;2019年全球灵芝市场规模为4.1亿美元&#xff0c;到2025年有望达到9.2亿美元。市场…

【NP】规约与问题复杂度

目录 多项式时间规约复杂度类 多项式时间规约 Polynomial-Time Reductions &#xff1a;如果问题 Y Y Y 的任意实例可以通过多项式次数的标准计算步骤&#xff0c;加上对解决问题 X X X 的黑盒的多项式次数调用来解决&#xff0c;那么称问题 Y Y Y 可以在多项式时间归约为问…

PyQt学习笔记

文章目录 1 环境搭建1.1 安装PyQt51.1.1 安装1.1.2 验证 1.2 安装PyInstaller1.3 安装PySide1.4 安装InnoSetup1.5 PyCharm配置外部工具1.5.1 PyCharm配置PyLUpdate1.5.2 PyCharm配置QtLinguist1.5.3 PyCharm配置QtDesigner1.5.4 PyCharm配置PyUIC1.5.5 PyCharm配置PyRCC1.5.6 …

“ManageEngine荣获Gartner SIEM客户选择四连冠“

我们非常激动地宣布&#xff0c;ManageEngine已经连续第四次被认定为Gartner Peer Insights‘Voice of the Customer’&#xff1a;安全信息与事件管理&#xff08;SIEM&#xff09;中的客户选择。这不仅是对我们卓越SIEM解决方案承诺的肯定&#xff0c;也延续了ManageEngine在…