濮阳杆衣贸易有限公司

主頁(yè) > 知識(shí)庫(kù) > Python與C/C++的相互調(diào)用案例

Python與C/C++的相互調(diào)用案例

熱門標(biāo)簽:南京crm外呼系統(tǒng)排名 crm電銷機(jī)器人 鄭州智能外呼系統(tǒng)中心 電銷機(jī)器人 金倫通信 云南地圖標(biāo)注 北京外呼電銷機(jī)器人招商 賓館能在百度地圖標(biāo)注嗎 400電話 申請(qǐng) 條件 汕頭電商外呼系統(tǒng)供應(yīng)商

一、問(wèn)題

Python模塊和C/C++的動(dòng)態(tài)庫(kù)間相互調(diào)用在實(shí)際的應(yīng)用中會(huì)有所涉及,在此作一總結(jié)。

二、Python調(diào)用C/C++

1、Python調(diào)用C動(dòng)態(tài)鏈接庫(kù)

Python調(diào)用C庫(kù)比較簡(jiǎn)單,不經(jīng)過(guò)任何封裝打包成so,再使用python的ctypes調(diào)用即可。

(1)C語(yǔ)言文件: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編譯生成動(dòng)態(tài)庫(kù)libpycall.so:gcc -o libpycall.so -shared -fPIC pycall.c。使用g++編譯生成C動(dòng)態(tài)庫(kù)的代碼中的函數(shù)或者方法時(shí),需要使用extern "C"來(lái)進(jìn)行編譯。

(3)Python調(diào)用動(dòng)態(tài)庫(kù)的文件:pycall.py

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

(4)運(yùn)行結(jié)果:

2、Python調(diào)用C++(類)動(dòng)態(tài)鏈接庫(kù)

需要extern "C"來(lái)輔助,也就是說(shuō)還是只能調(diào)用C函數(shù),不能直接調(diào)用方法,但是能解析C++方法。不是用extern "C",構(gòu)建后的動(dòng)態(tài)鏈接庫(kù)沒(méi)有這些函數(shù)的符號(hào)表。

(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:"aendl;
}
extern "C" {
 TestLib obj;
 void display() {
  obj.display(); 
  }
 void display_int() {
  obj.display(2); 
  }
}

(2)g++編譯生成動(dòng)態(tài)庫(kù)libpycall.so:g++ -o libpycallclass.so -shared -fPIC pycallclass.cpp。

(3)Python調(diào)用動(dòng)態(tài)庫(kù)的文件:pycallclass.py

import ctypes
so = ctypes.cdll.LoadLibrary 
lib = so("./libpycallclass.so") 
print 'display()'
lib.display()
print 'display(100)'
lib.display_int(100)

(4)運(yùn)行結(jié)果:

3、Python調(diào)用C/C++可執(zhí)行程序

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

#include iostream>
using namespace std;
int test()
{
 int a = 10, b = 5;
 return a+b;
}
int main()
{
 cout"---begin---"endl;
 int num = test();
 cout"num="numendl;
 cout"---end---"endl;
}

(2)編譯成二進(jìn)制可執(zhí)行文件:g++ -o testmain main.cpp。

(3) Python調(diào)用程序: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)運(yùn)行結(jié)果:

4、擴(kuò)展Python(C++為Python編寫擴(kuò)展模塊)

所有能被整合或?qū)氲狡渌黳ython腳本的代碼,都可以被稱為擴(kuò)展。可以用Python來(lái)寫擴(kuò)展,也可以用C和C++之類的編譯型的語(yǔ)言來(lái)寫擴(kuò)展。Python在設(shè)計(jì)之初就考慮到要讓模塊的導(dǎo)入機(jī)制足夠抽象。抽象到讓使用模塊的代碼無(wú)法了解到模塊的具體實(shí)現(xiàn)細(xì)節(jié)。Python的可擴(kuò)展性具有的優(yōu)點(diǎn):方便為語(yǔ)言增加新功能、具有可定制性、代碼可以實(shí)現(xiàn)復(fù)用等。

為 Python 創(chuàng)建擴(kuò)展需要三個(gè)主要的步驟:創(chuàng)建應(yīng)用程序代碼、利用樣板來(lái)包裝代碼和編譯與測(cè)試。

(1)創(chuàng)建應(yīng)用程序代碼

#include stdio.h>
#include stdlib.h>
#include string.h>
 
int fac(int n)
{
 if (n  2) return(1); /* 0! == 1! == 1 */
 return (n)*fac(n-1); /* n! == n*(n-1)! */
}
char *reverse(char *s)
{
 register char t,     /* tmp */
   *p = s,      /* fwd */
   *q = (s + (strlen(s) - 1)); /* 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(4));
 printf("8! == %d\n", fac(8));
 printf("12! == %d\n", fac(12));
 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 0;
}

上述代碼中有兩個(gè)函數(shù),一個(gè)是遞歸求階乘的函數(shù)fac();另一個(gè)reverse()函數(shù)實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的字符串反轉(zhuǎn)算法,其主要目的是修改傳入的字符串,使其內(nèi)容完全反轉(zhuǎn),但不需要申請(qǐng)內(nèi)存后反著復(fù)制的方法。

(2)用樣板來(lái)包裝代碼

接口的代碼被稱為“樣板”代碼,它是 應(yīng)用程序代碼與Python解釋器之間進(jìn)行交互所必不可少的一部分。樣板主要分為4步:a、包含Python的頭文件;b、為每個(gè)模塊的每一個(gè)函數(shù)增加一個(gè)型如PyObject* Module_func()的包裝函數(shù);c、為每個(gè)模塊增加一個(gè)型如PyMethodDef ModuleMethods[]的數(shù)組;d、增加模塊初始化函數(shù)void initModule()。

#include stdio.h>
#include stdlib.h>
#include string.h>
 
int fac(int n)
{
 if (n  2) return(1);
 return (n)*fac(n-1);
}
char *reverse(char *s)
{
 register char t,
   *p = s,
   *q = (s + (strlen(s) - 1));
 while (s  (p  q))
 {
  t = *p;
  *p++ = *q;
  *q-- = t;
 }
 return(s);
}
int test()
{
 char s[BUFSIZ];
 printf("4! == %d\n", fac(4));
 printf("8! == %d\n", fac(8));
 printf("12! == %d\n", fac(12));
 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 0;
}
#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);    #防止內(nèi)存泄漏
 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);
}

Python.h頭文件在大多數(shù)類Unix系統(tǒng)中會(huì)在/usr/local/include/python2.x或/usr/include/python2.x目錄中,系統(tǒng)一般都會(huì)知道文件安裝的路徑。

增加包裝函數(shù),所在模塊名為Extest,那么創(chuàng)建一個(gè)包裝函數(shù)叫Extest_fac(),在Python腳本中使用是先import Extest,然后調(diào)用Extest.fac(),當(dāng) Extest.fac()被調(diào)用時(shí),包裝函數(shù) Extest_fac()會(huì)被調(diào)用,包裝函數(shù)接受一個(gè) Python的整數(shù)參數(shù),把它轉(zhuǎn)為C的整數(shù),然后調(diào)用C的fac()函數(shù),得到一個(gè)整型的返回值,最后把這個(gè)返回值轉(zhuǎn)為Python的整型數(shù)做為整個(gè)函數(shù)調(diào)用的結(jié)果返回回去。其他兩個(gè)包裝函數(shù)Extest_doppel()和Extest_test()類似。

從Python到C的轉(zhuǎn)換用PyArg_Parse*系列函數(shù), int PyArg_ParseTuple():把Python傳過(guò)來(lái)的參數(shù)轉(zhuǎn)為C;int PyArg_ParseTupleAndKeywords()與PyArg_ParseTuple()作用相同,但是同時(shí)解析關(guān)鍵字參數(shù);它們 的用法跟C的sscanf函數(shù)很像,都接受一個(gè)字符串流,并根據(jù)一個(gè)指定的格式字符串進(jìn)行解析,把結(jié)果放入到相應(yīng)的指針?biāo)傅淖兞恐腥?,它們的返回值?表示解析成功,返回值為0表示失敗。 從C到Python的轉(zhuǎn)換函數(shù)是PyObject* Py_BuildValue():把C的數(shù)據(jù)轉(zhuǎn)為Python的一個(gè)對(duì)象或一組對(duì)象,然后返回之;Py_BuildValue的用法跟sprintf很像,把所有的參數(shù)按格式字符串所指定的格式轉(zhuǎn)換成一個(gè)Python的對(duì)象。

C與Python之間數(shù)據(jù)轉(zhuǎn)換的轉(zhuǎn)換代碼:

為每個(gè)模塊增加一個(gè)型如PyMethodDef ModuleMethods[]的數(shù)組,以便于Python解釋器能夠?qū)氩⒄{(diào)用它們,每一個(gè)數(shù)組都包含了函數(shù)在Python中的名字,相應(yīng)的包裝函數(shù)的名字以及一個(gè)METH_VARARGS常量,METH_VARARGS表示參數(shù)以tuple形式傳入。 若需要使用 PyArg_ParseTupleAndKeywords()函數(shù)來(lái)分析命名參數(shù)的話,還需要讓這個(gè)標(biāo)志常量與METH_KEYWORDS常量進(jìn)行邏輯與運(yùn)算常量 。數(shù)組最后用兩個(gè)NULL來(lái)表示函數(shù)信息列表的結(jié)束。

所有工作的最后一部分就是模塊的初始化函數(shù),調(diào)用Py_InitModule()函數(shù),并把模塊名和ModuleMethods[]數(shù)組的名字傳遞進(jìn)去,以便于解釋器能正確的調(diào)用模塊中的函數(shù)。

(3)編譯

為了讓新Python的擴(kuò)展能被創(chuàng)建,需要把它們與Python庫(kù)放在一起編譯,distutils包被用來(lái)編譯、安裝和分發(fā)這些模塊、擴(kuò)展和包。

創(chuàng)建一個(gè)setup.py 文件,編譯最主要的工作由setup()函數(shù)來(lái)完成:

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

Extension()第一個(gè)參數(shù)是(完整的)擴(kuò)展的名字,如果模塊是包的一部分的話,還要加上用'.'分隔的完整的包的名字。上述的擴(kuò)展是獨(dú)立的,所以名字只要寫"Extest"就行;sources參數(shù)是所有源代碼的文件列表,只有一個(gè)文件Extest2.c。setup需要兩個(gè)參數(shù):一個(gè)名字參數(shù)表示要編譯哪個(gè)內(nèi)容;另一個(gè)列表參數(shù)列出要編譯的對(duì)象,上述要編譯的是一個(gè)擴(kuò)展,故把ext_modules參數(shù)的值設(shè)為擴(kuò)展模塊的列表。

運(yùn)行setup.py build命令就可以開(kāi)始編譯我們的擴(kuò)展了,提示部分信息:

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

(4)導(dǎo)入和測(cè)試

你的擴(kuò)展會(huì)被創(chuàng)建在運(yùn)行setup.py腳本所在目錄下的build/lib.*目錄中,可以切換到那個(gè)目錄中來(lái)測(cè)試模塊,或者也可以用命令把它安裝到Python中:python setup.py install,會(huì)提示相應(yīng)信息。

測(cè)試模塊:

(5)引用計(jì)數(shù)和線程安全

Python對(duì)象引用計(jì)數(shù)的宏:Py_INCREF(obj)增加對(duì)象obj的引用計(jì)數(shù),Py_DECREF(obj)減少對(duì)象obj的引用計(jì)數(shù)。Py_INCREF()和Py_DECREF()兩個(gè)函數(shù)也有一個(gè)先檢查對(duì)象是否為空的版本,分別為Py_XINCREF()和Py_XDECREF()。

編譯擴(kuò)展的程序員必須要注意,代碼有可能會(huì)被運(yùn)行在一個(gè)多線程的Python環(huán)境中。這些線程使用了兩個(gè)C宏P(guān)y_BEGIN_ALLOW_THREADS和Py_END_ALLOW_THREADS, 通過(guò)將代碼和線程隔離,保證了運(yùn)行和非運(yùn)行時(shí)的安全性,由這些宏包裹的代碼將會(huì)允許其他線程的運(yùn)行。

三、C/C++調(diào)用Python

C++可以調(diào)用Python腳本,那么就可以寫一些Python的腳本接口供C++調(diào)用了,至少可以把Python當(dāng)成文本形式的動(dòng)態(tài)鏈接庫(kù),

需要的時(shí)候還可以改一改,只要不改變接口。缺點(diǎn)是C++的程序一旦編譯好了,再改就沒(méi)那么方便了。

(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系統(tǒng)前,必須使用Py_Initialize對(duì)其
 //進(jìn)行初始化。它會(huì)載入Python的內(nèi)建模塊并添加系統(tǒng)路
 //徑到模塊搜索路徑中。這個(gè)函數(shù)沒(méi)有返回值,檢查系統(tǒng)
 //是否初始化成功需要使用Py_IsInitialized。
 Py_Initialize();
 
 // 檢查初始化是否成功
 if ( !Py_IsInitialized() ) {
  return -1;
 }
 // 添加當(dāng)前路徑
 //把輸入的字符串作為Python代碼直接運(yùn)行,返回0
 //表示成功,-1表示有錯(cuò)。大多時(shí)候錯(cuò)誤都是因?yàn)樽址?
 //中有語(yǔ)法錯(cuò)誤。
 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 -1;
 }
 pDict = PyModule_GetDict(pModule);
 if ( !pDict ) {
  return -1;
 }
 
 // 找出函數(shù)名為add的函數(shù)
 printf("----------------------\n");
 pFunc = PyDict_GetItemString(pDict, "add");
 if ( !pFunc || !PyCallable_Check(pFunc) ) {
  printf("can't find function [add]");
  getchar();
  return -1;
  }
 
 // 參數(shù)進(jìn)棧
 PyObject *pArgs;
 pArgs = PyTuple_New(2);
 
 // PyObject* Py_BuildValue(char *format, ...)
 // 把C++的變量轉(zhuǎn)換成一個(gè)Python對(duì)象。當(dāng)需要從
 // C++傳遞變量到Python時(shí),就會(huì)使用這個(gè)函數(shù)。此函數(shù)
 // 有點(diǎn)類似C的printf,但格式不同。常用的格式有
 // s 表示字符串,
 // i 表示整型變量,
 // f 表示浮點(diǎn)數(shù),
 // O 表示一個(gè)Python對(duì)象。
 
 PyTuple_SetItem(pArgs, 0, Py_BuildValue("l",3));
 PyTuple_SetItem(pArgs, 1, Py_BuildValue("l",4));
 
 // 調(diào)用Python函數(shù)
 PyObject_CallObject(pFunc, pArgs);
 
 //下面這段是查找函數(shù)foo 并執(zhí)行foo
 printf("----------------------\n");
 pFunc = PyDict_GetItemString(pDict, "foo");
 if ( !pFunc || !PyCallable_Check(pFunc) ) {
  printf("can't find function [foo]");
  getchar();
  return -1;
  }
 
 pArgs = PyTuple_New(1);
 PyTuple_SetItem(pArgs, 0, Py_BuildValue("l",2)); 
 
 PyObject_CallObject(pFunc, pArgs);
  
 printf("----------------------\n");
 pFunc = PyDict_GetItemString(pDict, "update");
 if ( !pFunc || !PyCallable_Check(pFunc) ) {
  printf("can't find function [update]");
  getchar();
  return -1;
  }
 pArgs = PyTuple_New(0);
 PyTuple_SetItem(pArgs, 0, Py_BuildValue(""));
 PyObject_CallObject(pFunc, pArgs);  
 
 Py_DECREF(pName);
 Py_DECREF(pArgs);
 Py_DECREF(pModule);
 
 // 關(guān)閉Python
 Py_Finalize();
 return 0;
} 

(3)C++編譯成二進(jìn)制可執(zhí)行文件:g++ -o callpy callpy.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config -lpython2.6,編譯選項(xiàng)需要手動(dòng)指定Python的include路徑和鏈接接路徑(Python版本號(hào)根據(jù)具體情況而定)。

(4)運(yùn)行結(jié)果:

四、總結(jié)

(1)Python和C/C++的相互調(diào)用僅是測(cè)試代碼,具體的項(xiàng)目開(kāi)發(fā)還得參考Python的API文檔。

(2)兩者交互,C++可為Python編寫擴(kuò)展模塊,Python也可為C++提供腳本接口,更加方便于實(shí)際應(yīng)用。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

您可能感興趣的文章:
  • 膠水語(yǔ)言Python與C/C++的相互調(diào)用的實(shí)現(xiàn)
  • Python 調(diào)用C++封裝的進(jìn)一步探索交流
  • 一文助你搞懂參數(shù)傳遞原理解析(java、go、python、c++)
  • 詳解如何在VS2019和VScode中配置C++調(diào)用python接口
  • C++和python實(shí)現(xiàn)阿姆斯特朗數(shù)字查找實(shí)例代碼
  • python和C++共享內(nèi)存?zhèn)鬏攬D像的示例
  • Python擴(kuò)展C/C++庫(kù)的方法(C轉(zhuǎn)換為Python)
  • 解決c++調(diào)用python中文亂碼問(wèn)題
  • Python3安裝模塊報(bào)錯(cuò)Microsoft Visual C++ 14.0 is required的解決方法
  • C、C++、Java到Python,編程入門學(xué)習(xí)什么語(yǔ)言比較好
  • 如何在C++中調(diào)用Python

標(biāo)簽:浙江 石家莊 昆明 西寧 懷化 錫林郭勒盟 梅州 文山

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Python與C/C++的相互調(diào)用案例》,本文關(guān)鍵詞  Python,與,C++,的,相互,調(diào)用,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Python與C/C++的相互調(diào)用案例》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于Python與C/C++的相互調(diào)用案例的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    宁城县| 临武县| 嘉荫县| 云南省| SHOW| 南宫市| 永仁县| 巫山县| 池州市| 永和县| 兴仁县| 垦利县| 新乐市| 淮滨县| 石柱| 山阴县| 井冈山市| 华宁县| 通道| 珲春市| 乐山市| 宝清县| 丰镇市| 柳河县| 安图县| 九江县| 恩施市| 钦州市| 镇康县| 新邵县| 逊克县| 禄劝| 天台县| 柞水县| 丘北县| 增城市| 庐江县| 静宁县| 拜泉县| 普宁市| 玛多县|