[转] C++ 和 python之间的互相调用
转载自:https://www.cnblogs.com/apexchu/p/5015961.html
一、Python调用C/C++
1、Python调用C动态链接库
Python调用C库比较简单,不经过任何封装打包成so,再使用python的ctypes调用即可。
(1)C语言文件:pycall.c
- /***gcc -o libpycall.so -shared -fPIC pycall.c*/
- #include <stdio.h>
- #include <stdlib.h>
- int foo(int a, int b)
- {
- printf("you input %d and %d\n", a, b);
- return a+b;
- }
(2)gcc编译生成动态库libpycall.so:gcc -o libpycall.so -shared -fPIC pycall.c。使用g++编译生成C动态库的代码中的函数或者方法时,需要使用extern "C"来进行编译。
(3)Python调用动态库的文件:pycall.py
- import ctypes
- ll = ctypes.cdll.LoadLibrary
- lib = ll("./libpycall.so")
- lib.foo(1, 3)
- print '***finish***'
(4)运行结果:
2、Python调用C++(类)动态链接库
需要extern "C"来辅助,也就是说还是只能调用C函数,不能直接调用方法,但是能解析C++方法。不是用extern "C",构建后的动态链接库没有这些函数的符号表。
(1)C++类文件:pycallclass.cpp
- #include <iostream>
- using namespace std;
- class TestLib
- {
- public:
- void display();
- void display(int a);
- };
- void TestLib::display() {
- cout<<"First display"<<endl;
- }
- void TestLib::display(int a) {
- cout<<"Second display:"<<a<<endl;
- }
- extern "C" {
- TestLib obj;
- void display() {
- obj.display();
- }
- void display_int() {
- obj.display();
- }
- }
(3)Python调用动态库的文件:pycallclass.py
- import ctypes
- so = ctypes.cdll.LoadLibrary
- lib = so("./libpycallclass.so")
- print 'display()'
- lib.display()
- print 'display(100)'
- lib.display_int(100)
(4)运行结果
3、Python调用C/C++可执行程序
(1)C/C++程序:main.cpp
- #include <iostream>
- using namespace std;
- int test()
- {
- int a = , b = ;
- return a+b;
- }
- int main()
- {
- cout<<"---begin---"<<endl;
- int num = test();
- cout<<"num="<<num<<endl;
- cout<<"---end---"<<endl;
- }
(3)Python调用程序:main.py
- import commands
- import os
- main = "./testmain"
- if os.path.exists(main):
- rc, out = commands.getstatusoutput(main)
- print 'rc = %d, \nout = %s' % (rc, out)
- print '*'*10
- f = os.popen(main)
- data = f.readlines()
- f.close()
- print data
- print '*'*10
- os.system(main)
(4)运行结果:
4、扩展Python(C++为Python编写扩展模块)
所有能被整合或导入到其它python脚本的代码,都可以被称为扩展。可以用Python来写扩展,也可以用C和C++之类的编译型的语言来写扩展。Python在设计之初就考虑到要让模块的导入机制足够抽象。抽象到让使用模块的代码无法了解到模块的具体实现细节。Python的可扩展性具有的优点:方便为语言增加新功能、具有可定制性、代码可以实现复用等。
为 Python 创建扩展需要三个主要的步骤:创建应用程序代码、利用样板来包装代码和编译与测试。
(1)创建应用程序代码
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- int fac(int n)
- {
- if (n < ) return(); /* 0! == 1! == 1 */
- return (n)*fac(n-); /* n! == n*(n-1)! */
- }
- char *reverse(char *s)
- {
- register char t, /* tmp */
- *p = s, /* fwd */
- *q = (s + (strlen(s) - )); /* bwd */
- while (p < q) /* if p < q */
- {
- t = *p; /* swap & move ptrs */
- *p++ = *q;
- *q-- = t;
- }
- return(s);
- }
- int main()
- {
- char s[BUFSIZ];
- printf("4! == %d\n", fac());
- printf("8! == %d\n", fac());
- printf("12! == %d\n", fac());
- strcpy(s, "abcdef");
- printf("reversing 'abcdef', we get '%s'\n", \
- reverse(s));
- strcpy(s, "madam");
- printf("reversing 'madam', we get '%s'\n", \
- reverse(s));
- return ;
- }
上述代码中有两个函数,一个是递归求阶乘的函数fac();另一个reverse()函数实现了一个简单的字符串反转算法,其主要目的是修改传入的字符串,使其内容完全反转,但不需要申请内存后反着复制的方法。 (2)用样板来包装代码
接口的代码被称为“样板”代码,它是应用程序代码与Python解释器之间进行交互所必不可少的一部分。样板主要分为4步:a、包含Python的头文件;b、为每个模块的每一个函数增加一个型如PyObject*
Module_func()的包装函数;c、为每个模块增加一个型如PyMethodDef
ModuleMethods[]的数组;d、增加模块初始化函数void initModule()。
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- int fac(int n)
- {
- if (n < ) return();
- return (n)*fac(n-);
- }
- char *reverse(char *s)
- {
- register char t,
- *p = s,
- *q = (s + (strlen(s) - ));
- while (s && (p < q))
- {
- t = *p;
- *p++ = *q;
- *q-- = t;
- }
- return(s);
- }
- int test()
- {
- char s[BUFSIZ];
- printf("4! == %d\n", fac());
- printf("8! == %d\n", fac());
- printf("12! == %d\n", fac());
- strcpy(s, "abcdef");
- printf("reversing 'abcdef', we get '%s'\n", \
- reverse(s));
- strcpy(s, "madam");
- printf("reversing 'madam', we get '%s'\n", \
- reverse(s));
- return ;
- }
- #include "Python.h"
- static PyObject *
- Extest_fac(PyObject *self, PyObject *args)
- {
- int num;
- if (!PyArg_ParseTuple(args, "i", &num))
- return NULL;
- return (PyObject*)Py_BuildValue("i", fac(num));
- }
- static PyObject *
- Extest_doppel(PyObject *self, PyObject *args)
- {
- char *orig_str;
- char *dupe_str;
- PyObject* retval;
- if (!PyArg_ParseTuple(args, "s", &orig_str))
- return NULL;
- retval = (PyObject*)Py_BuildValue("ss", orig_str,
- dupe_str=reverse(strdup(orig_str)));
- free(dupe_str); #防止内存泄漏
- return retval;
- }
- static PyObject *
- Extest_test(PyObject *self, PyObject *args)
- {
- test();
- return (PyObject*)Py_BuildValue("");
- }
- static PyMethodDef
- ExtestMethods[] =
- {
- { "fac", Extest_fac, METH_VARARGS },
- { "doppel", Extest_doppel, METH_VARARGS },
- { "test", Extest_test, METH_VARARGS },
- { NULL, NULL },
- };
- void initExtest()
- {
- Py_InitModule("Extest", ExtestMethods);
- }
增加包装函数,所在模块名为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()函数来完成:
- #!/usr/bin/env python
- from distutils.core import setup, Extension
- MOD = 'Extest'
- setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest2.c'])])
运行setup.py build命令就可以开始编译我们的扩展了,提示部分信息:
- creating build/lib.linux-x86_64-2.6
- 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
- #test function
- def add(a,b):
- print "in python function add"
- print "a = " + str(a)
- print "b = " + str(b)
- print "ret = " + str(a+b)
- return
- def foo(a):
- print "in python function foo"
- print "a = " + str(a)
- print "ret = " + str(a * a)
- return
- class guestlist:
- def __init__(self):
- print "aaaa"
- def p():
- print "bbbbb"
- def __getitem__(self, id):
- return "ccccc"
- def update():
- guest = guestlist()
- print guest['aa']
- #update()
(2)C++代码:
- /**g++ -o callpy callpy.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config -lpython2.6**/
- #include <Python.h>
- int main(int argc, char** argv)
- {
- // 初始化Python
- //在使用Python系统前,必须使用Py_Initialize对其
- //进行初始化。它会载入Python的内建模块并添加系统路
- //径到模块搜索路径中。这个函数没有返回值,检查系统
- //是否初始化成功需要使用Py_IsInitialized。
- Py_Initialize();
- // 检查初始化是否成功
- if ( !Py_IsInitialized() ) {
- return -;
- }
- // 添加当前路径
- //把输入的字符串作为Python代码直接运行,返回0
- //表示成功,-1表示有错。大多时候错误都是因为字符串
- //中有语法错误。
- PyRun_SimpleString("import sys");
- PyRun_SimpleString("print '---import sys---'");
- PyRun_SimpleString("sys.path.append('./')");
- PyObject *pName,*pModule,*pDict,*pFunc,*pArgs;
- // 载入名为pytest的脚本
- pName = PyString_FromString("pytest");
- pModule = PyImport_Import(pName);
- if ( !pModule ) {
- printf("can't find pytest.py");
- getchar();
- return -;
- }
- pDict = PyModule_GetDict(pModule);
- if ( !pDict ) {
- return -;
- }
- // 找出函数名为add的函数
- printf("----------------------\n");
- pFunc = PyDict_GetItemString(pDict, "add");
- if ( !pFunc || !PyCallable_Check(pFunc) ) {
- printf("can't find function [add]");
- getchar();
- return -;
- }
- // 参数进栈
- *pArgs;
- pArgs = PyTuple_New();
- // PyObject* Py_BuildValue(char *format, ...)
- // 把C++的变量转换成一个Python对象。当需要从
- // C++传递变量到Python时,就会使用这个函数。此函数
- // 有点类似C的printf,但格式不同。常用的格式有
- // s 表示字符串,
- // i 表示整型变量,
- // f 表示浮点数,
- // O 表示一个Python对象。
- PyTuple_SetItem(pArgs, , Py_BuildValue("l",));
- PyTuple_SetItem(pArgs, , Py_BuildValue("l",));
- // 调用Python函数
- PyObject_CallObject(pFunc, pArgs);
- //下面这段是查找函数foo 并执行foo
- printf("----------------------\n");
- pFunc = PyDict_GetItemString(pDict, "foo");
- if ( !pFunc || !PyCallable_Check(pFunc) ) {
- printf("can't find function [foo]");
- getchar();
- return -;
- }
- pArgs = PyTuple_New();
- PyTuple_SetItem(pArgs, , Py_BuildValue("l",));
- PyObject_CallObject(pFunc, pArgs);
- printf("----------------------\n");
- pFunc = PyDict_GetItemString(pDict, "update");
- if ( !pFunc || !PyCallable_Check(pFunc) ) {
- printf("can't find function [update]");
- getchar();
- return -;
- }
- pArgs = PyTuple_New();
- PyTuple_SetItem(pArgs, , Py_BuildValue(""));
- PyObject_CallObject(pFunc, pArgs);
- Py_DECREF(pName);
- Py_DECREF(pArgs);
- Py_DECREF(pModule);
- // 关闭Python
- Py_Finalize();
- return ;
- }
(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之间的互相调用的更多相关文章
- shell和python之间的参数传递
我们在使用shell调用其他语言的程序的时候,希望能够便捷的从shell中输入参数,然后由目标程序接收参数并运行,这样就省去了每次需要在原程序进行修改带来的麻烦,这里介绍一下如何从shell中 ...
- Python与Javascript相互调用超详细讲解(2022年1月最新)(三)基本原理Part 3 - 通过C/C++联通
目录 TL; DR python调javascript javascript调python 原理 基于Node.js的javascript调用python 从Node调用python函数 V8 嵌入P ...
- [Python陷阱]os.system调用shell脚本获取返回值
当前有shell个脚本/tmp/test.sh,内容如下: #!/bin/bashexit 11 使用Python的os.system调用,获取返回值是: >>> ret=os.sy ...
- JAVA和C/C++之间的相互调用。
在一些Android应用的开发中,需要通过JNI和 Android NDK工具实现JAVA和C/C++之间的相互调用. Java Native Interface (JNI)标准是java平台的一部分 ...
- python中使用ctypes调用MinGW生成的动态链接库(dll)
关于gcc编译dll的我就不说了,网上举例一大堆,下面以g++为例. 假设有一个test.cpp文件如下: extern "C" { __declspec(dllexport) d ...
- 【Android进阶】Android程序与JavaScript之间的简单调用
本篇将讲解一个简单的Android与JavaScript之间的简单调用的小程序 效果图 工程结构 HTMLActivity.java代码 package com.example.javatojs; i ...
- 判断python对象是否可调用的三种方式及其区别
查找资料,基本上判断python对象是否为可调用的函数,有三种方法 使用内置的callable函数 callable(func) 用于检查对象是否可调用,返回True也可能调用失败,但是返回False ...
- Python 的 JPype 模块调用 Jar 包
背景与需求 最近学习并安装使用了HttpRunner框架去尝试做接口测试,并有后续在公司推广的打算. HttpRunner由Python开发,调用接口时需要依赖Python:而大多数公司的扩展工具包使 ...
- python基础--------字符串的调用详解(2)
Python 字符串的的调用方法~~~@@@ 17. strip : 去除字符串左右两边指定的字符 18. rstrip : 去除字符串右边指定的字符 19 . lstrip : 去除 ...
随机推荐
- 也说性能测试,顺便说python的多进程+多线程、协程
最近需要一个web系统进行接口性能测试,这里顺便说一下性能测试的步骤吧,大概如下 一.分析接口频率 根据系统的复杂程度,接口的数量有多有少,应该优先对那些频率高,数据库操作频繁的接口进行性能测试,所以 ...
- Oracle之with as和update用法
许久不用,又忘了,做个记录 update test b set b.code=(with t as(select t.id,code||'_'||row_number() over(partition ...
- nginx代理配置 配置中的静态资源配置,root 和 alias的区别
这篇主要内容是:nginx代理配置 配置中的静态资源配置,root 和 alias的区别.启动注意事项! 为什么会在window上配置了nginx呢?最近我们的项目是静态资源单独放在一个工程里面,后端 ...
- Codeforces 438E The Child and Binary Tree - 生成函数 - 多项式
题目传送门 传送点I 传送点II 传送点III 题目大意 每个点的权值$c\in {c_{1}, c_{2}, \cdots, c_{n}}$,问对于每个$1\leqslant s\leqslant ...
- SpringBootsad整合EhCache做缓存处理
轻量级的缓存框架Ehcache实现其功能.从以下几点切入: 什么是EhCache? 它和redis.membercache比较有什么优势? 和SpringBoot怎么整合? 实现机制? 有哪些坑? E ...
- 关于redis集群的问题no reachable node in cluster
重新启动redis集群时启动失败 n context with path [] threw exception [Request processing failed; nested exception ...
- HTML基础【4】:表格标签
表格标签 在过去表格标签用的非常非常的多,绝大多数的网站都是使用表格标签来制作的,也就是说表格标签是一个时代的代表 作用:以表格形式将数据显示出来,当数据量非常大的时候,表格这种展现形式被认为是最为清 ...
- java 保存到mysql数据库中文乱码
<property name="jdbcUrl">jdbc:mysql://localhost:3306/company?useUnicode=true&cha ...
- Redis 队列好处
Redis 队列好处 .降低流量高峰(并不是提升处理能力,系统的整体处理能力不变) .解除耦合(任务格式定好,各自演变,互不影响) .高可用(后台升级/崩溃完全不影响客户端的响应)
- [Android - QPST] 高通刷机/QPST刷机
参考网站: https://forum.xda-developers.com/zuk-z2-pro/how-to/howto-flash-stock-rom-t3435109 http://ask.l ...