国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Python - Python與C/C++的相互調用案例

Python與C/C++的相互調用案例

2021-09-14 00:25烏托邦2號 Python

這篇文章主要介紹了Python與C/C++的相互調用案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

一、問題

Python模塊和C/C++的動態庫間相互調用在實際的應用中會有所涉及,在此作一總結。

二、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
", 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)運行結果:

Python與C/C++的相互調用案例

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(2); 
  }
}

(2)g++編譯生成動態庫libpycall.so:g++ -o libpycallclass.so -shared -fPIC pycallclass.cpp。

(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)運行結果:

Python與C/C++的相互調用案例

3、Python調用C/C++可執行程序

(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="<<num<<endl;
 cout<<"---end---"<<endl;
}

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

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

import commands
import os
main = "./testmain"
if os.path.exists(main):
 rc, out = commands.getstatusoutput(main)
 print "rc = %d, 
out = %s" % (rc, out)
 
print "*"*10
f = os.popen(main) 
data = f.readlines() 
f.close() 
print data
 
print "*"*10
os.system(main)

(4)運行結果:

Python與C/C++的相互調用案例

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 < 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
", fac(4));
 printf("8! == %d
", fac(8));
 printf("12! == %d
", fac(12));
 strcpy(s, "abcdef");
 printf("reversing "abcdef", we get "%s"
", 
  reverse(s));
 strcpy(s, "madam");
 printf("reversing "madam", we get "%s"
", 
  reverse(s));
 return 0;
}

上述代碼中有兩個函數,一個是遞歸求階乘的函數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 < 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
", fac(4));
 printf("8! == %d
", fac(8));
 printf("12! == %d
", fac(12));
 strcpy(s, "abcdef");
 printf("reversing "abcdef", we get "%s"
", 
  reverse(s));
 strcpy(s, "madam");
 printf("reversing "madam", we get "%s"
", 
  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);    #防止內存泄漏
 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頭文件在大多數類Unix系統中會在/usr/local/include/python2.x或/usr/include/python2.x目錄中,系統一般都會知道文件安裝的路徑。

增加包裝函數,所在模塊名為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之間數據轉換的轉換代碼:

Python與C/C++的相互調用案例

為每個模塊增加一個型如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"])])

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

運行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.6 -o build/lib.linux-x86_64-2.6/Extest.so

(4)導入和測試

你的擴展會被創建在運行setup.py腳本所在目錄下的build/lib.*目錄中,可以切換到那個目錄中來測試模塊,或者也可以用命令把它安裝到Python中:python setup.py install,會提示相應信息。

測試模塊:

Python與C/C++的相互調用案例

(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 -1;
 }
 // 添加當前路徑
 //把輸入的字符串作為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 -1;
 }
 pDict = PyModule_GetDict(pModule);
 if ( !pDict ) {
  return -1;
 }
 
 // 找出函數名為add的函數
 printf("----------------------
");
 pFunc = PyDict_GetItemString(pDict, "add");
 if ( !pFunc || !PyCallable_Check(pFunc) ) {
  printf("can"t find function [add]");
  getchar();
  return -1;
  }
 
 // 參數進棧
 PyObject *pArgs;
 pArgs = PyTuple_New(2);
 
 // PyObject* Py_BuildValue(char *format, ...)
 // 把C++的變量轉換成一個Python對象。當需要從
 // C++傳遞變量到Python時,就會使用這個函數。此函數
 // 有點類似C的printf,但格式不同。常用的格式有
 // s 表示字符串,
 // i 表示整型變量,
 // f 表示浮點數,
 // O 表示一個Python對象。
 
 PyTuple_SetItem(pArgs, 0, Py_BuildValue("l",3));
 PyTuple_SetItem(pArgs, 1, Py_BuildValue("l",4));
 
 // 調用Python函數
 PyObject_CallObject(pFunc, pArgs);
 
 //下面這段是查找函數foo 并執行foo
 printf("----------------------
");
 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("----------------------
");
 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);
 
 // 關閉Python
 Py_Finalize();
 return 0;
} 

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

(4)運行結果:

Python與C/C++的相互調用案例

四、總結

(1)Python和C/C++的相互調用僅是測試代碼,具體的項目開發還得參考Python的API文檔。

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

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。如有錯誤或未考慮完全的地方,望不吝賜教。

原文鏈接:https://blog.csdn.net/taiyang1987912/article/details/44779719

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 老熟妇午夜毛片一区二区三区 | 日韩免费 | 欧美精品在线一区 | 欧美日韩亚洲二区 | 男女涩涩| 免费一级片免费一级片 | 免费亚洲网站 | 亚洲精品久久久一区二区三区 | 国产精品久久久久久久免费大片 | 欧美日韩亚洲国产精品 | 国产激情精品视频 | 成人精品一区二区三区 | 一级久久| 人人添人人添 | 大白屁股一区二区视频 | 国内精品视频 | 免费视频一区二区 | 成人亚洲欧美 | 视频一区二区三 | 久久精品成人 | 亚洲成人一级片 | 欧美国产日韩一区 | 免费的av| 欧美不卡一区二区三区 | 一区二区三区四区在线播放 | 成人免费一区二区三区视频软件 | 一级片在线观看 | 中文字幕亚洲视频 | 欧美色影院 | 色噜| 国产精品视频一区二区三区不卡 | 日韩精品一区二区在线观看 | 亚洲精选久久 | 亚洲 自拍 另类 欧美 丝袜 | av男人的天堂在线 | 亚洲一卡二卡 | 精品国产精品三级精品av网址 | 激情欧美一区二区三区中文字幕 | 99手机在线视频 | 欧美黑人一级爽快片淫片高清 | 91精品国产一区二区三区四区在线 |