转载自:https://www.cnblogs.com/apexchu/p/5015961.html

一、Python调用C/C++

1、Python调用C动态链接库

Python调用C库比较简单,不经过任何封装打包成so,再使用python的ctypes调用即可。
(1)C语言文件:pycall.c

  1. /***gcc -o libpycall.so -shared -fPIC pycall.c*/
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. int foo(int a, int b)
  5. {
  6. printf("you input %d and %d\n", a, b);
  7. return a+b;
  8. }

(2)gcc编译生成动态库libpycall.so:gcc -o libpycall.so -shared -fPIC pycall.c。使用g++编译生成C动态库的代码中的函数或者方法时,需要使用extern "C"来进行编译。

(3)Python调用动态库的文件:pycall.py

  1. import ctypes
  2. ll = ctypes.cdll.LoadLibrary
  3. lib = ll("./libpycall.so")
  4. lib.foo(1, 3)
  5. print '***finish***'

(4)运行结果:

2、Python调用C++(类)动态链接库

需要extern "C"来辅助,也就是说还是只能调用C函数,不能直接调用方法,但是能解析C++方法。不是用extern "C",构建后的动态链接库没有这些函数的符号表。
(1)C++类文件:pycallclass.cpp

  1. #include <iostream>
  2. using namespace std;
  3.  
  4. class TestLib
  5. {
  6. public:
  7. void display();
  8. void display(int a);
  9. };
  10. void TestLib::display() {
  11. cout<<"First display"<<endl;
  12. }
  13.  
  14. void TestLib::display(int a) {
  15. cout<<"Second display:"<<a<<endl;
  16. }
  17. extern "C" {
  18. TestLib obj;
  19. void display() {
  20. obj.display();
  21. }
  22. void display_int() {
  23. obj.display();
  24. }
  25. }

(3)Python调用动态库的文件:pycallclass.py

  1. import ctypes
  2. so = ctypes.cdll.LoadLibrary
  3. lib = so("./libpycallclass.so")
  4. print 'display()'
  5. lib.display()
  6. print 'display(100)'
  7. lib.display_int(100)

(4)运行结果

3、Python调用C/C++可执行程序

(1)C/C++程序:main.cpp

  1. #include <iostream>
  2. using namespace std;
  3. int test()
  4. {
  5. int a = , b = ;
  6. return a+b;
  7. }
  8. int main()
  9. {
  10. cout<<"---begin---"<<endl;
  11. int num = test();
  12. cout<<"num="<<num<<endl;
  13. cout<<"---end---"<<endl;
  14. }

(3)Python调用程序:main.py

  1. import commands
  2. import os
  3. main = "./testmain"
  4. if os.path.exists(main):
  5. rc, out = commands.getstatusoutput(main)
  6. print 'rc = %d, \nout = %s' % (rc, out)
  7.  
  8. print '*'*10
  9. f = os.popen(main)
  10. data = f.readlines()
  11. f.close()
  12. print data
  13.  
  14. print '*'*10
  15. os.system(main)

(4)运行结果:

4、扩展Python(C++为Python编写扩展模块)

所有能被整合或导入到其它python脚本的代码,都可以被称为扩展。可以用Python来写扩展,也可以用C和C++之类的编译型的语言来写扩展。Python在设计之初就考虑到要让模块的导入机制足够抽象。抽象到让使用模块的代码无法了解到模块的具体实现细节。Python的可扩展性具有的优点:方便为语言增加新功能、具有可定制性、代码可以实现复用等。
       为 Python 创建扩展需要三个主要的步骤:创建应用程序代码、利用样板来包装代码和编译与测试。
(1)创建应用程序代码

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. int fac(int n)
  6. {
  7. if (n < ) return(); /* 0! == 1! == 1 */
  8. return (n)*fac(n-); /* n! == n*(n-1)! */
  9. }
  10.  
  11. char *reverse(char *s)
  12. {
  13. register char t, /* tmp */
  14. *p = s, /* fwd */
  15. *q = (s + (strlen(s) - )); /* bwd */
  16.  
  17. while (p < q) /* if p < q */
  18. {
  19. t = *p; /* swap & move ptrs */
  20. *p++ = *q;
  21. *q-- = t;
  22. }
  23. return(s);
  24. }
  25.  
  26. int main()
  27. {
  28. char s[BUFSIZ];
  29. printf("4! == %d\n", fac());
  30. printf("8! == %d\n", fac());
  31. printf("12! == %d\n", fac());
  32. strcpy(s, "abcdef");
  33. printf("reversing 'abcdef', we get '%s'\n", \
  34. reverse(s));
  35. strcpy(s, "madam");
  36. printf("reversing 'madam', we get '%s'\n", \
  37. reverse(s));
  38. return ;
  39. }

上述代码中有两个函数,一个是递归求阶乘的函数fac();另一个reverse()函数实现了一个简单的字符串反转算法,其主要目的是修改传入的字符串,使其内容完全反转,但不需要申请内存后反着复制的方法。 (2)用样板来包装代码
       
接口的代码被称为“样板”代码,它是应用程序代码与Python解释器之间进行交互所必不可少的一部分。样板主要分为4步:a、包含Python的头文件;b、为每个模块的每一个函数增加一个型如PyObject*
Module_func()的包装函数;c、为每个模块增加一个型如PyMethodDef
ModuleMethods[]的数组;d、增加模块初始化函数void initModule()。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. int fac(int n)
  6. {
  7. if (n < ) return();
  8. return (n)*fac(n-);
  9. }
  10.  
  11. char *reverse(char *s)
  12. {
  13. register char t,
  14. *p = s,
  15. *q = (s + (strlen(s) - ));
  16.  
  17. while (s && (p < q))
  18. {
  19. t = *p;
  20. *p++ = *q;
  21. *q-- = t;
  22. }
  23. return(s);
  24. }
  25.  
  26. int test()
  27. {
  28. char s[BUFSIZ];
  29. printf("4! == %d\n", fac());
  30. printf("8! == %d\n", fac());
  31. printf("12! == %d\n", fac());
  32. strcpy(s, "abcdef");
  33. printf("reversing 'abcdef', we get '%s'\n", \
  34. reverse(s));
  35. strcpy(s, "madam");
  36. printf("reversing 'madam', we get '%s'\n", \
  37. reverse(s));
  38. return ;
  39. }
  40.  
  41. #include "Python.h"
  42.  
  43. static PyObject *
  44. Extest_fac(PyObject *self, PyObject *args)
  45. {
  46. int num;
  47. if (!PyArg_ParseTuple(args, "i", &num))
  48. return NULL;
  49. return (PyObject*)Py_BuildValue("i", fac(num));
  50. }
  51.  
  52. static PyObject *
  53. Extest_doppel(PyObject *self, PyObject *args)
  54. {
  55. char *orig_str;
  56. char *dupe_str;
  57. PyObject* retval;
  58.  
  59. if (!PyArg_ParseTuple(args, "s", &orig_str))
  60. return NULL;
  61. retval = (PyObject*)Py_BuildValue("ss", orig_str,
  62. dupe_str=reverse(strdup(orig_str)));
  63. free(dupe_str); #防止内存泄漏
  64. return retval;
  65. }
  66.  
  67. static PyObject *
  68. Extest_test(PyObject *self, PyObject *args)
  69. {
  70. test();
  71. return (PyObject*)Py_BuildValue("");
  72. }
  73.  
  74. static PyMethodDef
  75. ExtestMethods[] =
  76. {
  77. { "fac", Extest_fac, METH_VARARGS },
  78. { "doppel", Extest_doppel, METH_VARARGS },
  79. { "test", Extest_test, METH_VARARGS },
  80. { NULL, NULL },
  81. };
  82.  
  83. void initExtest()
  84. {
  85. Py_InitModule("Extest", ExtestMethods);
  86. }

增加包装函数,所在模块名为Extest,那么创建一个包装函数叫Extest_fac(),在Python脚本中使用是先import Extest,然后调用Extest.fac(),当Extest.fac()被调用时,包装函数Extest_fac()会被调用,包装函数接受一个 Python的整数参数,把它转为C的整数,然后调用C的fac()函数,得到一个整型的返回值,最后把这个返回值转为Python的整型数做为整个函数调用的结果返回回去。其他两个包装函数Extest_doppel()和Extest_test()类似。
        
从Python到C的转换用PyArg_Parse*系列函数,int
PyArg_ParseTuple():把Python传过来的参数转为C;int
PyArg_ParseTupleAndKeywords()与PyArg_ParseTuple()作用相同,但是同时解析关键字参数;它们的用法跟C的sscanf函数很像,都接受一个字符串流,并根据一个指定的格式字符串进行解析,把结果放入到相应的指针所指的变量中去,它们的返回值为1表示解析成功,返回值为0表示失败。从C到Python的转换函数是PyObject*

Py_BuildValue():把C的数据转为Python的一个对象或一组对象,然后返回之;Py_BuildValue的用法跟sprintf很像,把所有的参数按格式字符串所指定的格式转换成一个Python的对象。
        C与Python之间数据转换的转换代码:

为每个模块增加一个型如PyMethodDef ModuleMethods[]的数组,以便于Python解释器能够导入并调用它们,每一个数组都包含了函数在Python中的名字,相应的包装函数的名字以及一个METH_VARARGS常量,METH_VARARGS表示参数以tuple形式传入。 若需要使用PyArg_ParseTupleAndKeywords()函数来分析命名参数的话,还需要让这个标志常量与METH_KEYWORDS常量进行逻辑与运算常量 。数组最后用两个NULL来表示函数信息列表的结束。
         所有工作的最后一部分就是模块的初始化函数,调用Py_InitModule()函数,并把模块名和ModuleMethods[]数组的名字传递进去,以便于解释器能正确的调用模块中的函数。
(3)编译
        为了让新Python的扩展能被创建,需要把它们与Python库放在一起编译,distutils包被用来编译、安装和分发这些模块、扩展和包。
        创建一个setup.py 文件,编译最主要的工作由setup()函数来完成:

  1. #!/usr/bin/env python
  2.  
  3. from distutils.core import setup, Extension
  4.  
  5. MOD = 'Extest'
  6. setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest2.c'])])

运行setup.py build命令就可以开始编译我们的扩展了,提示部分信息:

  1. creating build/lib.linux-x86_64-2.6
  2. gcc -pthread -shared build/temp.linux-x86_64-2.6/Extest2.o -L/usr/lib64 -lpython2. -o build/lib.linux-x86_64-2.6/Extest.so

你的扩展会被创建在运行setup.py脚本所在目录下的build/lib.*目录中,可以切换到那个目录中来测试模块,或者也可以用命令把它安装到Python中:python setup.py install,会提示相应信息。
         测试模块:

(5)引用计数和线程安全
    
Python对象引用计数的宏:Py_INCREF(obj)增加对象obj的引用计数,Py_DECREF(obj)减少对象obj的引用计数。Py_INCREF()和Py_DECREF()两个函数也有一个先检查对象是否为空的版本,分别为Py_XINCREF()和Py_XDECREF()。

编译扩展的程序员必须要注意,代码有可能会被运行在一个多线程的Python环境中。这些线程使用了两个C宏Py_BEGIN_ALLOW_THREADS和Py_END_ALLOW_THREADS,通过将代码和线程隔离,保证了运行和非运行时的安全性,由这些宏包裹的代码将会允许其他线程的运行。

二、C/C++调用Python

C++可以调用Python脚本,那么就可以写一些Python的脚本接口供C++调用了,至少可以把Python当成文本形式的动态链接库, 
需要的时候还可以改一改,只要不改变接口。缺点是C++的程序一旦编译好了,再改就没那么方便了。
(1)Python脚本:pytest.py

  1. #test function
  2. def add(a,b):
  3. print "in python function add"
  4. print "a = " + str(a)
  5. print "b = " + str(b)
  6. print "ret = " + str(a+b)
  7. return
  8.  
  9. def foo(a):
  10.  
  11. print "in python function foo"
  12. print "a = " + str(a)
  13. print "ret = " + str(a * a)
  14. return
  15.  
  16. class guestlist:
  17. def __init__(self):
  18. print "aaaa"
  19. def p():
  20. print "bbbbb"
  21. def __getitem__(self, id):
  22. return "ccccc"
  23. def update():
  24. guest = guestlist()
  25. print guest['aa']
  26.  
  27. #update()

(2)C++代码:

  1. /**g++ -o callpy callpy.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config -lpython2.6**/
  2. #include <Python.h>
  3. int main(int argc, char** argv)
  4. {
  5. // 初始化Python
  6. //在使用Python系统前,必须使用Py_Initialize对其
  7. //进行初始化。它会载入Python的内建模块并添加系统路
  8. //径到模块搜索路径中。这个函数没有返回值,检查系统
  9. //是否初始化成功需要使用Py_IsInitialized。
  10. Py_Initialize();
  11.  
  12. // 检查初始化是否成功
  13. if ( !Py_IsInitialized() ) {
  14. return -;
  15. }
  16. // 添加当前路径
  17. //把输入的字符串作为Python代码直接运行,返回0
  18. //表示成功,-1表示有错。大多时候错误都是因为字符串
  19. //中有语法错误。
  20. PyRun_SimpleString("import sys");
  21. PyRun_SimpleString("print '---import sys---'");
  22. PyRun_SimpleString("sys.path.append('./')");
  23. PyObject *pName,*pModule,*pDict,*pFunc,*pArgs;
  24.  
  25. // 载入名为pytest的脚本
  26. pName = PyString_FromString("pytest");
  27. pModule = PyImport_Import(pName);
  28. if ( !pModule ) {
  29. printf("can't find pytest.py");
  30. getchar();
  31. return -;
  32. }
  33. pDict = PyModule_GetDict(pModule);
  34. if ( !pDict ) {
  35. return -;
  36. }
  37.  
  38. // 找出函数名为add的函数
  39. printf("----------------------\n");
  40. pFunc = PyDict_GetItemString(pDict, "add");
  41. if ( !pFunc || !PyCallable_Check(pFunc) ) {
  42. printf("can't find function [add]");
  43. getchar();
  44. return -;
  45. }
  46.  
  47. // 参数进栈
  48. *pArgs;
  49. pArgs = PyTuple_New();
  50.  
  51. // PyObject* Py_BuildValue(char *format, ...)
  52. // 把C++的变量转换成一个Python对象。当需要从
  53. // C++传递变量到Python时,就会使用这个函数。此函数
  54. // 有点类似C的printf,但格式不同。常用的格式有
  55. // s 表示字符串,
  56. // i 表示整型变量,
  57. // f 表示浮点数,
  58. // O 表示一个Python对象。
  59.  
  60. PyTuple_SetItem(pArgs, , Py_BuildValue("l",));
  61. PyTuple_SetItem(pArgs, , Py_BuildValue("l",));
  62.  
  63. // 调用Python函数
  64. PyObject_CallObject(pFunc, pArgs);
  65.  
  66. //下面这段是查找函数foo 并执行foo
  67. printf("----------------------\n");
  68. pFunc = PyDict_GetItemString(pDict, "foo");
  69. if ( !pFunc || !PyCallable_Check(pFunc) ) {
  70. printf("can't find function [foo]");
  71. getchar();
  72. return -;
  73. }
  74.  
  75. pArgs = PyTuple_New();
  76. PyTuple_SetItem(pArgs, , Py_BuildValue("l",));
  77.  
  78. PyObject_CallObject(pFunc, pArgs);
  79.  
  80. printf("----------------------\n");
  81. pFunc = PyDict_GetItemString(pDict, "update");
  82. if ( !pFunc || !PyCallable_Check(pFunc) ) {
  83. printf("can't find function [update]");
  84. getchar();
  85. return -;
  86. }
  87. pArgs = PyTuple_New();
  88. PyTuple_SetItem(pArgs, , Py_BuildValue(""));
  89. PyObject_CallObject(pFunc, pArgs);
  90.  
  91. Py_DECREF(pName);
  92. Py_DECREF(pArgs);
  93. Py_DECREF(pModule);
  94.  
  95. // 关闭Python
  96. Py_Finalize();
  97. return ;
  98. }

(3)C++编译成二进制可执行文件:g++ -o callpy callpy.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config -lpython2.6,编译选项需要手动指定Python的include路径和链接接路径(Python版本号根据具体情况而定)。

(4)运行结果:

[转] C++ 和 python之间的互相调用的更多相关文章

  1. shell和python之间的参数传递

        我们在使用shell调用其他语言的程序的时候,希望能够便捷的从shell中输入参数,然后由目标程序接收参数并运行,这样就省去了每次需要在原程序进行修改带来的麻烦,这里介绍一下如何从shell中 ...

  2. Python与Javascript相互调用超详细讲解(2022年1月最新)(三)基本原理Part 3 - 通过C/C++联通

    目录 TL; DR python调javascript javascript调python 原理 基于Node.js的javascript调用python 从Node调用python函数 V8 嵌入P ...

  3. [Python陷阱]os.system调用shell脚本获取返回值

    当前有shell个脚本/tmp/test.sh,内容如下: #!/bin/bashexit 11 使用Python的os.system调用,获取返回值是: >>> ret=os.sy ...

  4. JAVA和C/C++之间的相互调用。

    在一些Android应用的开发中,需要通过JNI和 Android NDK工具实现JAVA和C/C++之间的相互调用. Java Native Interface (JNI)标准是java平台的一部分 ...

  5. python中使用ctypes调用MinGW生成的动态链接库(dll)

    关于gcc编译dll的我就不说了,网上举例一大堆,下面以g++为例. 假设有一个test.cpp文件如下: extern "C" { __declspec(dllexport) d ...

  6. 【Android进阶】Android程序与JavaScript之间的简单调用

    本篇将讲解一个简单的Android与JavaScript之间的简单调用的小程序 效果图 工程结构 HTMLActivity.java代码 package com.example.javatojs; i ...

  7. 判断python对象是否可调用的三种方式及其区别

    查找资料,基本上判断python对象是否为可调用的函数,有三种方法 使用内置的callable函数 callable(func) 用于检查对象是否可调用,返回True也可能调用失败,但是返回False ...

  8. Python 的 JPype 模块调用 Jar 包

    背景与需求 最近学习并安装使用了HttpRunner框架去尝试做接口测试,并有后续在公司推广的打算. HttpRunner由Python开发,调用接口时需要依赖Python:而大多数公司的扩展工具包使 ...

  9. python基础--------字符串的调用详解(2)

    Python 字符串的的调用方法~~~@@@ 17.  strip  : 去除字符串左右两边指定的字符 18.   rstrip : 去除字符串右边指定的字符 19 .   lstrip  :  去除 ...

随机推荐

  1. 也说性能测试,顺便说python的多进程+多线程、协程

    最近需要一个web系统进行接口性能测试,这里顺便说一下性能测试的步骤吧,大概如下 一.分析接口频率 根据系统的复杂程度,接口的数量有多有少,应该优先对那些频率高,数据库操作频繁的接口进行性能测试,所以 ...

  2. Oracle之with as和update用法

    许久不用,又忘了,做个记录 update test b set b.code=(with t as(select t.id,code||'_'||row_number() over(partition ...

  3. nginx代理配置 配置中的静态资源配置,root 和 alias的区别

    这篇主要内容是:nginx代理配置 配置中的静态资源配置,root 和 alias的区别.启动注意事项! 为什么会在window上配置了nginx呢?最近我们的项目是静态资源单独放在一个工程里面,后端 ...

  4. Codeforces 438E The Child and Binary Tree - 生成函数 - 多项式

    题目传送门 传送点I 传送点II 传送点III 题目大意 每个点的权值$c\in {c_{1}, c_{2}, \cdots, c_{n}}$,问对于每个$1\leqslant s\leqslant ...

  5. SpringBootsad整合EhCache做缓存处理

    轻量级的缓存框架Ehcache实现其功能.从以下几点切入: 什么是EhCache? 它和redis.membercache比较有什么优势? 和SpringBoot怎么整合? 实现机制? 有哪些坑? E ...

  6. 关于redis集群的问题no reachable node in cluster

    重新启动redis集群时启动失败 n context with path [] threw exception [Request processing failed; nested exception ...

  7. HTML基础【4】:表格标签

    表格标签 在过去表格标签用的非常非常的多,绝大多数的网站都是使用表格标签来制作的,也就是说表格标签是一个时代的代表 作用:以表格形式将数据显示出来,当数据量非常大的时候,表格这种展现形式被认为是最为清 ...

  8. java 保存到mysql数据库中文乱码

    <property name="jdbcUrl">jdbc:mysql://localhost:3306/company?useUnicode=true&cha ...

  9. Redis 队列好处

    Redis 队列好处 .降低流量高峰(并不是提升处理能力,系统的整体处理能力不变) .解除耦合(任务格式定好,各自演变,互不影响) .高可用(后台升级/崩溃完全不影响客户端的响应)

  10. [Android - QPST] 高通刷机/QPST刷机

    参考网站: https://forum.xda-developers.com/zuk-z2-pro/how-to/howto-flash-stock-rom-t3435109 http://ask.l ...