C++ 调用Python脚本及其第三方库

C++ 调用Python脚本及其第三方库

  • 〇、项目说明
  • 一、添加环境变量
    • 1. PYTHONHOME
      • 系统环境变量
      • 代码临时变量
    • 2. PYTHONPATH
      • 系统环境变量
      • 临时环境变量
    • 3. base环境添加到Path
  • 二、 新建VS工程
    • 添加新属性表
      • 包含目录
      • 库目录
      • 依赖项
  • 三、 C++调用Python
    • 1. 执行简单python语句
    • 2. 调用Python脚本中的函数
    • 3. 调用整个python脚本
    • 4. C++调用python打包的exe
  • 四、 其他
    • const char* 转wchar_t *
    • 各类报错
    • 一些参考

〇、项目说明

目标是用C++调用Python脚本,该脚本中可能包含各种第三方库,需要使用对应conda环境下的Python解释器。以下将由简单到复杂展示如何使用C++调用Anaconda下的python及其第三方库(以Anaconda 的base环境为例)。

一、添加环境变量

以下PYTHONHOME和PYTHONPATH如果使用系统环境变量方式添加,会影响其他虚拟环境下python的使用,如果需要经常切换虚拟环境或不想反复改动系统环境变量,建议使用代码临时变量的方式添加。

1. PYTHONHOME

系统环境变量

N: PYTHONHOME
V: D:\ProgramData\Anaconda3

C++ 调用Python脚本及其第三方库_第1张图片

代码临时变量

#include 

    // 设置 PYTHONHOME 环境变量
    const wchar_t* PYTHONHOME_N = L"PYTHONHOME";
    const wchar_t* PYTHONHOME_V = L"D:\\ProgramData\\Anaconda3";

    if (!SetEnvironmentVariable(PYTHONHOME_N ,PYTHONHOME_V )) {
        std::cerr << "Failed to set PYTHONHOME environment variable." << std::endl;
        return 1;
    }

2. PYTHONPATH

系统环境变量

N: PYTHONPATH
V: %PYTHONHOME%\Lib;%PYTHONHOME%\DLLs

C++ 调用Python脚本及其第三方库_第2张图片

临时环境变量

#include 

	// 设置 PYTHONPATH 环境变量
    const wchar_t* PYTHONPATH_N = L"PYTHONPATH";
    const wchar_t* PYTHONPATH_V = L"D:\\ProgramData\\Anaconda3\\Lib;D:\\ProgramData\\Anaconda3\\DLLs";

    if (!SetEnvironmentVariable(PYTHONPATH_N ,PYTHONPATH_V )) {
        std::cerr << "Failed to set PYTHONHOME environment variable." << std::endl;
        return 1;
    }

3. base环境添加到Path

  1. 先建立系统环境变量Anaconda_Python_base
N: Anaconda_Python_base
V: D:\ProgramData\Anaconda3;D:\ProgramData\Anaconda3\libs;D:\ProgramData\Anaconda3\include;D:\ProgramData\Anaconda3\Scripts;D:\ProgramData\Anaconda3\Library\bin;

C++ 调用Python脚本及其第三方库_第3张图片

再在Path中追加刚新建的环境变量:
在这里插入图片描述

二、 新建VS工程

工程名为 UsingPython。

添加新属性表

注意,属性表和平台对应,我将使用 Release | x64。
在对应文件夹位置右击—添加新项目属性表,我命名为Python_base_Release_x64。
C++ 调用Python脚本及其第三方库_第4张图片
接下来双击该属性表,对其进行修改。

包含目录

工程—属性—配置属性— C/C++ —常规—附加包含目录

D:\ProgramData\Anaconda3\include

库目录

工程—属性—配置属性—链接器—常规—附加库目录

D:\ProgramData\Anaconda3\libs

依赖项

工程—属性—配置属性—链接器—输入—附加依赖项

python39.lib

三、 C++调用Python

1. 执行简单python语句

使用系统环境变量,新建一个usingpython_01.cpp。

#include "Python.h"

int main()
{
	Py_Initialize();		//初始化

	PyRun_SimpleString("print('Hello python, i am cpp.')");

	Py_Finalize();		//释放
}

执行,成功调用Python解释器进行文本输出。
C++ 调用Python脚本及其第三方库_第5张图片

2. 调用Python脚本中的函数

在代码中使用临时环境变量,新建一个usingpython_02.cpp。接下来将调用octree_utils.py里的show_octree()函数:

import numpy as np
import open3d as o3d

def show_octree(pts_path,max_depth=5):
    """通过点云求open3d的体素集"""
    pts = np.loadtxt(pts_path, delimiter=" ")
    # 生成点云数据
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(pts[:, :3])

    # 加入颜色信息
    if (len(pts[0, :]) > 5):
        color = pts[:, 3:6] / 255.0
        pcd.colors = o3d.utility.Vector3dVector(color)  # 将颜色值归一化到[0, 1]

    octree = o3d.geometry.Octree(max_depth)
    octree.convert_from_point_cloud(pcd)
    o3d.visualization.draw_geometries([octree])
    return 1

C++代码:

#include "Python.h"
#include 
#include 
#include 
using namespace std;


int main()
{
    // 设置 PYTHONHOME 环境变量
    const wchar_t* PYTHONHOME_N = L"PYTHONHOME";
    const wchar_t* PYTHONHOME_V = L"D:\\ProgramData\\Anaconda3";
    if (!SetEnvironmentVariableW(PYTHONHOME_N ,PYTHONHOME_V )) {
        std::cerr << "Failed to set PYTHONHOME environment variable." << std::endl;
        return 1;
    }

	// 设置 PYTHONPATH 环境变量
    const wchar_t* PYTHONPATH_N = L"PYTHONPATH";
    const wchar_t* PYTHONPATH_V = L"D:\\ProgramData\\Anaconda3\\Lib;D:\\ProgramData\\Anaconda3\\DLLs";
    if (!SetEnvironmentVariableW(PYTHONPATH_N ,PYTHONPATH_V )) {
        std::cerr << "Failed to set PYTHONHOME environment variable." << std::endl;
        return 1;
    }


    Py_Initialize();    // 初始化

    // 将py文件所在路径添加到搜索路径中
	PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./')");

    // 加载模块
    PyObject* pModule = PyImport_ImportModule("octree_utils");      //文件名 octree_utils.py
    if (!pModule) 
    {
        cout << "[ERROR] Python get module failed." << endl;
        return 0;
    }
    else {
        cout << "[INFO] Python get module succeed." << endl;
    }

    // 加载函数
    PyObject* pv = PyObject_GetAttrString(pModule, "show_octree");
    if (!pv || !PyCallable_Check(pv)) 
    {
        cout << "[ERROR] Can't find funftion." << endl;
        return 0;
    }
    cout << "[INFO] Get function succeed." << endl;


    // 准备参数
    const char* arg1 = "D:\\Project\\CPP_Project\\UsingPython\\UsingPython\\module\\library.txt";
    int arg2 = 5;


    // 将参数转换为 Python 对象
    PyObject* pArgs = PyTuple_New(2);
    PyTuple_SetItem(pArgs, 0, PyUnicode_FromString(arg1));
    PyTuple_SetItem(pArgs, 1, PyLong_FromLong(arg2));  

    // 调用函数
    PyObject* pRet = PyObject_CallObject(pv, pArgs);

    // 获取参数
    if (pRet)  // 验证是否调用成功
    {
        long result = PyLong_AsLong(pRet);
        cout << "result:" << result;
    }

    Py_Finalize();      // 释放资源

    return 0;
}

运行cpp代码,用C++调用python中open3d库计算八叉树并显示。
C++ 调用Python脚本及其第三方库_第6张图片

3. 调用整个python脚本

接下来将调用整个python脚本,其实就是相当于开一个终端执行脚本。

python文件内容如下,功能为打印传入的参数:

import sys
def printArgs():
    for arg in sys.argv:
        print(arg)

if __name__ == '__main__':
    printArgs()

在代码中使用临时环境变量,新建一个usingpython_03.cpp,内容如下:

#include "Python.h"
#include 
#include 
#include 
#include 
using namespace std;

int main()
{

    // 设置 PYTHONHOME 环境变量
    const wchar_t* PYTHONHOME_N = L"PYTHONHOME";
    const wchar_t* PYTHONHOME_V = L"D:\\ProgramData\\Anaconda3";
    if (!SetEnvironmentVariableW(PYTHONHOME_N ,PYTHONHOME_V )) {
        std::cerr << "Failed to set PYTHONHOME environment variable." << std::endl;
        return 1;
    }

	// 设置 PYTHONPATH 环境变量
    const wchar_t* PYTHONPATH_N = L"PYTHONPATH";
    const wchar_t* PYTHONPATH_V = L"D:\\ProgramData\\Anaconda3\\Lib;D:\\ProgramData\\Anaconda3\\DLLs";
    if (!SetEnvironmentVariableW(PYTHONPATH_N ,PYTHONPATH_V )) {
        std::cerr << "Failed to set PYTHONHOME environment variable." << std::endl;
        return 1;
    }


    Py_Initialize();

    PyRun_SimpleString("import os");

    //执行调用脚本文件命令,注意文件的路径
    if (PyRun_SimpleString("os.system(('python ./octree_utils.py  usingPython 20230517'))") == NULL)
    {
        return -1;
    }

    Py_Finalize();

    return 0;
}

测试成功。
C++ 调用Python脚本及其第三方库_第7张图片

4. C++调用python打包的exe

这种方法跟调用其他exe一样,在这里暂不展开,放一种常用方法:

#include 
 
ShellExecute(NULL,L"open",L"./test.exe",NULL,NULL,SW_SHOW);

四、 其他

const char* 转wchar_t *

由于C++调用Python时很多API的参数类型为wchar_t*,所以我让chatGPT写了一个const char* 转wchar_t *的函数。

#include 
#include 
#include 
#include 
using namespace std;

wchar_t* ConvertToWideString(const char* str) {
    // 获取所需的宽字符长度
    size_t length = mbstowcs(nullptr, str, 0);
    if (length == static_cast<size_t>(-1)) {
        // 错误处理
        return nullptr;
    }
    
    // 分配足够的内存来存储宽字符
    wchar_t* wideStr = new wchar_t[length + 1];
    
    // 执行转换
    if (mbstowcs(wideStr, str, length + 1) == static_cast<size_t>(-1)) {
        // 错误处理
        delete[] wideStr;
        return nullptr;
    }
    
    return wideStr;
}

各类报错

对遇到部分报错记录了一下,基本上按以上方法设置完成后都可以避免。

  1. Fatal Python error…
Python path configuration:
  PYTHONHOME = (not set)
  PYTHONPATH = (not set)
  program name = 'python'
  isolated = 0
  environment = 1
  user site = 1
  import site = 1
  sys._base_executable = 'D:\\Project\\CPP_Project\\UsingPython\\x64\\Release\\UsingPython.exe'
  sys.base_prefix = 'D:\\ProgramData\\Anaconda3'
  sys.base_exec_prefix = 'D:\\ProgramData\\Anaconda3'
  sys.platlibdir = 'lib'
  sys.executable = 'D:\\Project\\CPP_Project\\UsingPython\\x64\\Release\\UsingPython.exe'
  sys.prefix = 'D:\\ProgramData\\Anaconda3'
  sys.exec_prefix = 'D:\\ProgramData\\Anaconda3'
  sys.path = [
    'D:\\ProgramData\\Anaconda3\\python39.zip',
    '.\\DLLs',
    '.\\lib',
    'D:\\Project\\CPP_Project\\UsingPython\\x64\\Release',
  ]
Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
Python runtime state: core initialized
ModuleNotFoundError: No module named 'encodings'

解决方案
见以上PYTHONHOMEPYTHONPATH环境变量设置,建议使用代码临时变量

  1. UserWarning: mkl-service package failed to import…
D:\ProgramData\Anaconda3\Lib\site-packages\numpy\__init__.py:148: UserWarning: mkl-service package failed to import, therefore Intel(R) MKL initialization ensuring its correct out-of-the box operation under condition when Gnu OpenMP had already been loaded by Python process is not assured. Please install mkl-service package, see http://github.com/IntelPython/mkl-service
  from . import _distributor_init

解决方案
见以上base环境添加到Path

  1. Traceback …
Traceback (most recent call last):
  File "", line 1, in <module>
NameError: name 'execfile' is not defined

解决方案
这个其实是python3删除了execfile方法,改用exec方法,但这个方法需要先把代码读下来再执行,所以我在上面调用整个python脚本使用了另一种os.system()方法。


一些参考

参考1、参考2、参考3、参考4

你可能感兴趣的:(C++,Python,python,c++)