c++ 和 python 混合编程 (使用ctypes 传递数字 字符 字符串 数组)

本文详细介绍了在Windows 10环境下,如何安装Python 3.7 32位版本,配置Visual Studio 2013环境,以及C++如何调用Python和Python调用C++的步骤,包括不同调用方式和库设置。实例演示了字符串、数组和JSON数据的交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

本文介绍C++(vs2013)和 Python (3.7 32bit)编程的方式
文章作者在进行学习后,将整个过程进行记录,供大家参考,也备自己查询


一、安装python 和 设置visual studio 2013

1.在win10上安装python

1.0初遇问题:

本机环境 win10 + visual studio2013 + vs code
笔者下载了 python3.7后,安装到本机,并设置了系统的环境变量Path后(设置方法:此电脑–>右键单击->属性->关于->高级系统设置->“环境变量”)。在CMD命令行下,输入Python.exe. windows10依然提示要到Microsof App Store 下载最新版本的Python。此处怀疑windows 的cmd 对python命令进行了特殊处理

解决:
依然安装自己的python3.7 32bit 。通过设置来解决程序调用python.exe的问题(后面有提到)

1.1.下载python37 32.bit

笔者因环境要求,所以必须使用32bit. (注意visual studio 2013 编译的程序如果为32bit时 ,其对应的python也必须使用32bit的)
Python 下载(python.org)
开始安装,记录python的安装路径(例如:d:\Program Files (x86)\Python\Python37-32)

1.2 设置vs2013的环境

新建MFCApplication(EXE)
1.建立的向导: 单文档, MBCS(非unicode) , 静态依赖MFC
2.并将项目属性“VC++目录”设置如下

设置内容详细参考路径
1.可执行文件目录python.exe(3.7 32bit)所在路径d:\Program Files (x86)\Python\Python37-32
2.包含文件目录python.h所在路径d:\Program Files (x86)\Python\Python37-32\include
3.库目录python37.lib所在路径d:\Program Files (x86)\Python\Python37-32\libs (注意不是LIB文件夹)

在这里插入图片描述

另外,还应设置系统的环境变量Path中增加 d:\Program Files (x86)\Python\Python37-32 这个目录

1.3 设置vs code

A.进入vs code ,新建callc.py文件。
B.在设置->Command Palette 中 输入 Python:Select Interpreter
在这里插入图片描述
C.选中Python 3.7.0 32-bit (可以注意到本机安装了多个python)
请大家注意可能每次启动vs code 都需要此步设置
在这里插入图片描述

二、C++ 调用 Python

1.简单原理

c++可以调用python的脚本,实际上是c++将python的解释器加入到了程序中(通过python37.dll).

2.配置c++所需要的库

设置附加依赖: python37.lib
在这里插入图片描述

3 代码

1.需要使用头文件 “python.h”
2.初始化函数 Py_Initialize
3.加载模块 PyImport_ImportModule
4.找到函数 PyObject_GetAttrString
5.设置参数Py_BuildValue
6.调用函数 PyObject_CallObject
7.返回值解析 PyArg_Parse

c++文件部分

#include "python.h"

void myMain()
{
	// Python解释器初始化
	Py_Initialize();
	// 打开.py文件。注意参数是"callc"而不是"callc.py"
	PyObject* pModule = PyImport_ImportModule("callc");
	if (pModule)
	{
		// 读取文件中的函数,参数为函数名
		PyObject* pFunc = PyObject_GetAttrString(pModule, "func1");
		if (pFunc)
		{
			// 构建参数,包含两个double
			PyObject* args = Py_BuildValue("(dd)", 1.5, 2.0);
			// 调用函数得到返回值
			PyObject* res = PyObject_CallObject(pFunc, args);
			if (!res)
			{
				// 打印错误信息
				PyErr_Print();
			}
			else
			{
				// 从PyObject中得到返回值
				double dr;
				PyArg_Parse(res, "d", &dr);
				CString str;
				str.Format("%f", dr);
				AfxMessageBox(str);
			}
		}
	}
	// 关闭Python解释器
	Py_Finalize();
}

python文件 callc.py

# 本文件名为 callc.py
def func1(a, b):
    return a + b;

三 Python 调用 C++

0.新建MfcLibray DLL

visual studio 使用MFC DLL模板,新建一个DLL,为Python提供可调用函数
选项有: DLL, MBCS(非unicode) , 静态依赖MFC ,

1.ctyps 库

ctyps库提供了可以将python的值(数字/字符串等)进行打包传递给c++函数.(ctypes是python自带库)

2.实现python调用 c++

2.1 python部分

from ctypes import *

def testString(): # 测试字符串数据 试用ctypes.CDLL加载
    dll = CDLL(r'D:\CodeTest\c_py\MFCApplication1\MFCApplication1\Release\MFCLibrary1.dll') # dll的全路径,也可以使用相对路径
    string = "汉字"
    dll.test2(c_char_p(string.encode('utf-8'))) # 将"汉字"这两个字先转换为utf8 然后使用c_char_p做成缓存传递给test2函数

2.2 c++部分

笔者现有c++程序是用MSBC(多字节)程序.所以从python传来的字符串需要经历两次转换: utf8(python) ->unicode (w_char)->多字节(char) .这样的转换试用ConvertUTF8ToANSI函数
如果c++程序是用unicode的,仅需要 utf8(python) ->unicode (w_char) ,这样的转换需要 UTF82Wide 这个函数


// 被python调用的函数,接受一个字符串.并试用MessageBox把字符串显示出来
extern "C"	__declspec(dllexport) int test2(std::string test_string) //test_string 内容为utf8字符串
{
	CString str;
	ConvertUTF8ToANSI(test_string.c_str(), str); // 将python utf8字符串转 ansi 字符串
	AfxMessageBox(str);  //当前mfc DLL  是 MBCS(非Unicode)
	return 0;
}

//下面介绍两种utf8字符串的转换函数
//1. Utf8 转 ansi 
void ConvertUTF8ToANSI(const char* strUTF8, CString &strANSI)
{
	int nLen = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCTSTR)strUTF8, -1, NULL, 0);  //返回需要的unicode长度  
	WCHAR * wszANSI = new WCHAR[nLen + 1];
	memset(wszANSI, 0, nLen * 2 + 2);
	nLen = MultiByteToWideChar(CP_UTF8, 0, (LPCTSTR)strUTF8, -1, wszANSI, nLen); //把utf8转成unicode  

	nLen = WideCharToMultiByte(CP_ACP, 0, wszANSI, -1, NULL, 0, NULL, NULL); //得到要的ansi长度  

	char *szANSI = new char[nLen + 1];
	memset(szANSI, 0, nLen + 1);
	WideCharToMultiByte(CP_ACP, 0, wszANSI, -1, szANSI, nLen, NULL, NULL); //把unicode转成ansi  

	strANSI = szANSI;

	delete wszANSI;

	delete szANSI;

}

//2. Utf8 转 wide - 本文不使用,仅作对比讲解
std::wstring UTF82Wide(const std::string& strUTF8)
{
    int nWide = ::MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), strUTF8.size(), NULL, 0);

    std::unique_ptr<wchar_t[]> buffer(new wchar_t[nWide + 1]);
    if (!buffer)
    {
        return L"";
    }

    ::MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), strUTF8.size(), buffer.get(), nWide);
    buffer[nWide] = L'\0';

    return buffer.get();
}
 

2.3 重点讲解 “调用方式” 和 “导出”

1 ctypes.CDLL() 和 ctypes.WinDLL() 加载DLL方式

C++ 的函数现有两种调用约定:__cdecl 和 __stdcall
其对应的python ctypes有两种加载方法,如下

c++ 函数调用方式python ctypes加载方式
__cdeclctypes.CDLL(dll名字)dll = CDLL(r’MFCLibrary1.dll’)
__stdcallctypes.WinDLL(dll名字)
  1. c++ 函数的调用声明方式
    前两种为 __cdecl, 最后一种为 __stdcall
  extern "C"	__declspec(dllexport) int test2(std::string test_string); //c++默认 __cdecl    py:ctypes.CDLL
  
  extern "C"	__declspec(dllexport) int __cdecl test3(std::string test_string); // __cdecl    py:ctypes.CDLL

  extern "C"	__declspec(dllexport) int  __stdcall test4(std::string test_string) //__stdcall  py:ctypes.WinDLL
标志作用
extern “C”是为了不改名,函数仅加 _ 下划线必须使用,否则编译器会把函数改名.
__declspec(dllexport)是为了导出函数,可以被外部找到否则 dll 外部无法找到
__cdeclvs 默认的调用约定上述 test2 函数没有该标志,其实默认就如此,用cytpes.CDll加载
__stdcallstdcall调用约定需要使用 ctype.WinDll 加载dll 才可以

2.4 CDll调用方式

python 代码

from ctypes import *

def testString3():
    dll = WinDLL(r'D:\CodeTest\c_py\MFCApplication1\MFCApplication1\Release\MFCLibrary1.dll')
    string = "汉字3"
    dll.test3(c_char_p(string.encode('utf-8')))

c++ 代码

extern "C" __declspec(dllexport) int __cdecl test3(std::string test_string)  //此处也可以不写 __cdecl, c++默认 __cdecl 
{
	CString str;
	ConvertUTF8ToANSI(test_string.c_str(), str);
	AfxMessageBox(str);
	return 0;
}

2.5 WinDll调用方式

python 代码

from ctypes import *

def testString4():
    dll = WinDLL(r'D:\CodeTest\c_py\MFCApplication1\MFCApplication1\Release\MFCLibrary1.dll')
    string = "汉字4"
    dll.test4(c_char_p(string.encode('utf-8')))

c++ 代码

extern "C" __declspec(dllexport) int __stdcall test4(std::string test_string)
{
	CString str;
	ConvertUTF8ToANSI(test_string.c_str(), str);
	AfxMessageBox(str);
	return 0;
}

3 例子: python int float string 数组 传递

def testtest(): # 多种数据测试通过
    dll = CDLL(r'D:\CodeTest\c_py\MFCApplication1\MFCApplication1\Release\MFCLibrary1.dll')
    char_p_test = bytes("汉字","utf8")#汉字需用采用utf8编码
    #int_arr4 = c_int*4
    #int_arr = int_arr4()
    int_arr = (c_int*4)()  # 上面的简化写法
    int_arr[0] = 1
    int_arr[1] = 3
    int_arr[2] = 5
    int_arr[3] = 9
    char_arr2 = c_char*2
    char_arr22 = char_arr2*2
    char_arr22a = char_arr22()
    char_arr22a[0][0] = b'a'
    char_arr22a[0][1]=  b'b'
    char_arr22a[1][0] = b'c'
    char_arr22a[1][1] = b'd'
    dll.test(9999,'a',char_p_test,int_arr,char_arr22a)
extern "C" __declspec(dllexport) int test(int int_test, char char_test, char *test_string, int int_arr[4], char char_arr2[2][2])
{
		//因为c++程序试用mbcs 所以字符串需要转换为ANSI
		CString str;
		ConvertUTF8ToANSI(test_string, str); //如果程序本身试用unicode 则需要调用UTF82Wide函数 见2.2
		AfxMessageBox(str);
		
		//char_arr2 字符串数组中的每条字符串也需要进行ConvertUTF8ToANSI转换
}

4 例子 C++ 使用 python的返回的字符串 形成json数据格式

# testMod.py
def testJson(n):  # 返回json字符串解析通过
    data2 = json.dumps({'a': 'testA', 'b': 7}, sort_keys=True, indent=4, separators=(',', ': '))
    xx='none'
    return data2,xx  # 需要返回两个,以保证字符串返回正确

#include "json/json.h"

void myMain()
{
// Python解释器初始化
	Py_Initialize();
 
	PyObject* pModule = PyImport_ImportModule("testMod");
	if (pModule)
	{
		// 读取文件中的函数,参数为函数名
		PyObject* pFunc = PyObject_GetAttrString(pModule, "testJson");
		if (pFunc)
		{
			// 构建参数,包含1个int
			PyObject* pArgs = Py_BuildValue("(i)", 0);
			// 调用函数得到返回值
			PyObject* res = PyObject_CallObject(pFunc, pArgs);
			if (!res)
			{
				// 打印错误信息
				PyErr_Print();
			}
			else
			{
				char *p = NULL;
				char *none = NULL;
				PyArg_ParseTuple(res, "s|s", &p,&none); //接收两个字符串
				CString strAnsi;
			
				Json::Value root;
				Json::Reader reader;
				if (!reader.parse(p, root))
				{
					Py_Finalize();
					return;
				}
				CString ss = root["a"].asCString();
				//可以正确返回 testA
			}
		}
	}
	// 关闭Python解释器
	Py_Finalize();
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值