分享

C++开发实战(五):用C/C++给python写一个模块_如何在c++中写python

 禁忌石 2023-10-14 发布于浙江

一、前言

1、上一篇文章,我们已经对C/C++工程进行了二次封装,并生成了可用的python模块

2、本篇文章将基于上一篇文章的思路,自己写一个python模块

C++开发实战(四):对提供接口的C/C++进行二次开发_Lion King的博客-CSDN博客_c++二次开发https://blog.csdn.net/weixin_43431593/article/details/121615034?spm=1001.2014.3001.5501

二、写一个只有函数对象的python模块

1、创建工程(动态库项目,windows动态库)

 

2、 初步配置开发环境

 

 

 

 

 3、编写程序

dllmain.cpp

  1. // dllmain.cpp : 定义 DLL 应用程序的入口点。
  2. #include "pch.h"
  3. BOOL APIENTRY DllMain( HMODULE hModule,
  4. DWORD ul_reason_for_call,
  5. LPVOID lpReserved
  6. )
  7. {
  8. switch (ul_reason_for_call)
  9. {
  10. case DLL_PROCESS_ATTACH:
  11. case DLL_THREAD_ATTACH:
  12. case DLL_THREAD_DETACH:
  13. case DLL_PROCESS_DETACH:
  14. break;
  15. }
  16. return TRUE;
  17. }
  18. // 函数本体,实现加法
  19. PyObject* cpp_sum(PyObject*, PyObject* args)
  20. {
  21. printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__); // 打印日志
  22. int num = 0; // 用于接收传入的整型数值
  23. PyArg_ParseTuple(args, "i", &num); // python元组,解析传入的参数
  24. long long sum = 0; // 设置输出结果为长整形
  25. for (int i = 1; i <= num; i++)
  26. sum += i;
  27. printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__); // 打印日志
  28. return Py_BuildValue("L", sum); // 返回长整型数值
  29. };
  30. PyMethodDef onedllMethods[] =
  31. {
  32. {"sum", cpp_sum, METH_VARARGS, "sum 1 to num!"}, // 方法名称(供外部调用),函数本体(C函数),可变参数,方法说明
  33. {NULL, NULL, 0, NULL} // 表示结尾,全员为空
  34. };
  35. // 命名可任意
  36. struct PyModuleDef onedllModule =
  37. {
  38. PyModuleDef_HEAD_INIT, // 固定写法
  39. "onedll", // 与模块名称(项目名称)一致
  40. "onedll python test module!", // 模块说明,help该模块时给出的提示信息,不要用中文
  41. -1, // 固定格式
  42. //方法
  43. onedllMethods
  44. };
  45. PyMODINIT_FUNC // 返回python函数对象
  46. PyInit_onedll() // 命名一定要加上工程名称,即PyInit_工程名(模块名)
  47. {
  48. return PyModule_Create(&onedllModule/*模块信息*/);
  49. };

4、编译输出

(1)配置输出python库的文件类型

(2)编译

 (3)使用编译好的python库

 5、问题解决

(1)python不是debug版本,没有debug的东西,因此报如下错误:

Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL)
Python runtime state: unknown
(2)以”初步配置开发环境“的方式配置Release环境

 

 

 6、遗留一个问题:模块迁移到其他环境产生的问题

(1)在其他电脑的python环境,导入该模块,提示找不到指定模块,原因是导入DLL失败,说明咋们的这个模块不完整或python版本不兼容,先放着,后续再解决。

>>> import onedll
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: DLL load failed while importing onedll: 找不到指定的模块。

三、写一个包含类对象的python模块

1、编写程序

dllmain.cpp

  1. // dllmain.cpp : 定义 DLL 应用程序的入口点。
  2. #include "pch.h"
  3. #include <string> // 增加字符串,供类使用
  4. using namespace std; // 增加命名空间
  5. void CppClassDel(PyObject* obj); // 声明一个被调用的函数
  6. BOOL APIENTRY DllMain( HMODULE hModule,
  7. DWORD ul_reason_for_call,
  8. LPVOID lpReserved
  9. )
  10. {
  11. switch (ul_reason_for_call)
  12. {
  13. case DLL_PROCESS_ATTACH:
  14. case DLL_THREAD_ATTACH:
  15. case DLL_THREAD_DETACH:
  16. case DLL_PROCESS_DETACH:
  17. break;
  18. }
  19. return TRUE;
  20. }
  21. class CppClass
  22. {
  23. public:
  24. // 构造函数
  25. CppClass() {
  26. printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  27. };
  28. // 析构函数
  29. ~CppClass() {
  30. printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  31. };
  32. double test(const string& s) {
  33. printf("%s(%d):%s <%s>\r\n", __FILE__, __LINE__, __FUNCTION__, s.c_str());
  34. return 99.99;
  35. };
  36. };
  37. // 构造函数
  38. PyObject* CppClassInit(PyObject*, PyObject* args)
  39. {
  40. printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  41. return PyCapsule_New(new CppClass, "CppClass", CppClassDel);
  42. };
  43. // 析构函数
  44. void CppClassDel(PyObject* obj)
  45. {
  46. printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  47. CppClass* tmp = (CppClass*)PyCapsule_GetPointer(obj, "CppClass");
  48. delete tmp;
  49. };
  50. PyObject* CppClassTest(PyObject*, PyObject* args) {
  51. PyObject* thiz = NULL;
  52. string str;
  53. str.resize(1024);
  54. char* pstr = (char*)str.c_str();
  55. PyArg_ParseTuple(args, "Os", &thiz, &pstr);
  56. CppClass* _this = (CppClass*)PyCapsule_GetPointer(thiz, "CppClass");
  57. double ret = _this->test(str);
  58. return Py_BuildValue("d", ret);
  59. };
  60. // 函数本体,实现加法
  61. PyObject* cpp_sum(PyObject*, PyObject* args)
  62. {
  63. printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__); // 打印日志
  64. int num = 0; // 用于接收传入的整型数值
  65. PyArg_ParseTuple(args, "i", &num); // python元组,解析传入的参数
  66. long long sum = 0; // 设置输出结果为长整形
  67. for (int i = 1; i <= num; i++)
  68. sum += i;
  69. printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__); // 打印日志
  70. return Py_BuildValue("L", sum); // 返回长整型数值
  71. };
  72. PyMethodDef onedllMethods[] =
  73. {
  74. {"sum", cpp_sum, METH_VARARGS, "sum 1 to num!"}, // 方法名称(供外部调用),函数本体(C函数),可变参数,方法说明
  75. {"CppClassInit", CppClassInit, METH_VARARGS, "CppClass()"},
  76. {"CppClassTest", CppClassTest, METH_VARARGS, "double Test(string str)"},
  77. {NULL, NULL, 0, NULL} // 表示结尾,全员为空
  78. };
  79. // 命名可任意
  80. struct PyModuleDef onedllModule =
  81. {
  82. PyModuleDef_HEAD_INIT, // 固定写法
  83. "onedll", // 与模块名称(项目名称)一致
  84. "onedll python test module!", // 模块说明,help该模块时给出的提示信息,不要用中文
  85. -1, // 固定格式
  86. //方法
  87. onedllMethods
  88. };
  89. PyMODINIT_FUNC // 返回python函数对象
  90. PyInit_onedll() // 命名一定要加上工程名称,即PyInit_工程名(模块名)
  91. {
  92. return PyModule_Create(&onedllModule/*模块信息*/);
  93. };

2、编写测试程序

run_test.py

  1. from onedll import *
  2. class CppClass:
  3. def __init__(self):
  4. self.thiz=CppClassInit()
  5. def my_test(self, s):
  6. return CppClassTest(self.thiz, s)
  7. c=CppClass()
  8. a=c.my_test("g")
  9. print(a)

3、遗留一个问题:python传入的字符串没有被打印出来

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多