摘要:調用以回調函數地址為參數的函數這個主題就稍微繞一些了,也就是說在接口中,需要傳入回調函數作為參數。這個問題在中也可以解決,并且回調函數可以用定義。代碼代碼很簡單回調函數的傳入參數為,返回參數也是。
項目中要對一個用 C 編寫的 .so 庫進行邏輯自測。這項工作,考慮到靈活性,我首先考慮用 Python 來完成。
研究了一些資料,采用 python 的 ctypes 來完成這項工作。已經驗證通過,本文記錄一下適配流程。驗證采用 cpp 來設計,不過暫時還沒有涉及類的內容。以后如果需要再補足。
本文地址:https://segmentfault.com/a/1190000013339754
參考資料 ctypes以下資料是關于 ctypes 的,也就是本文采用的資料:
Python的學習(三十二)---- ctypes庫的使用整理
Python Ctypes 結構體指針處理(函數參數,函數返回)
ctypes庫
用Python ctypes 建立與C的介面
Python調用C/C++動態鏈接庫的方法詳解
【轉】python中使用 C 類型的數組以及ctypes 的用法
ctypes
將函數指針轉換為可調用對象
Python Ctypes結構體指針處理(函數參數,函數返回)
Can"t install python-dev on centos 6.5
Python 3.5, ctypes: TypeError: bytes or integer address expected instead of str instance
一些 Python 本身的資料由于研究 ctypes 時我用的是 Python 2.7,后來切換到 Python 3 的時候稍微遇到一點適配問題,因此也順便記錄一下我切換過程中參考的一些資料:
python多線程ctrl-c退出問題
Python多線程之怎樣優雅的響應中斷異常(Ctrl+C)
CentOS7.2 多個python版本共存
Python 2 和 Python 3 有哪些主要區別? - 豬了個去的回答 - 知乎
關于 python ImportError: No module named 的問題
python的模塊加載和路徑查找
如何獲得Python腳本所在目錄的位置
關于python中帶下劃線的變量和函數 的意義
【變量】關于python中的下劃線
16.16. ctypes — A foreign function library for Python
其他 python 調用 C 的方法Python 調用 C 還有其他的幾個解決方案,比如 cython、SWIG 等等。但是查了不少資料沒能解決我的兩個關鍵訴求(結構體參數和回調函數):
Python調用C
Python.h:No such file or directory
環境準備 ctypes 包準備使用 ctypes,需要首先安裝 python-dev 包:
Ubuntu: $ sudo apt-get install python-dev -y CentOS: $ sudo yum install python-devel -y
這里主要包含了 ctypes 包。
.so 文件準備將你的 C 代碼編譯成 .so 文件。這里假設目標文件是 libtest.so,放在工作目錄下。
基本參數函數調用首先是最簡單的函數調用,并且函數參數為基本數據類型。待調用的函數定義如下:
extern "C" int max(int a, int b) { return (a > b) ? a : b; }
這種情況下,在 Python 中的調用就很簡單了。我們需要使用 ctypes 包中的 cdll 模塊加載 .so 文件,然后就可以調用庫中的函數了。
Python 代碼如下:
#!/usr/bin/python3 # -*- coding: UTF-8 -*- from ctypes import * so_file = cdll.LoadLibrary("./libtest.so") # 如果前文使用的是 import ctypes,則這里應該是 ctypes.cdll.LoadLobrary(...) ret = so_file.max(22, 20) print("so_file class:", type(so_file)) print("so_file.max =", ret)
輸出:
so_file class:調用以結構體為參數的函數so_file.max = 22
這就稍微復雜點了,因為 C 語言中的結構體在 Python 中并沒有直接一一對應。不過不用擔心,簡單而言,解決方案就是:在 Python 代碼中調用 ctypes 的類進行 Python 化的封裝。
網上的代碼進行了最簡化的演示,這里我從這一小節開始,建議讀者把一個 .so 文件,封裝成 Python 模塊。這樣一來庫的包裝更加簡潔和清晰。
這里是 C 代碼的部分,主要是結構體的聲明。用于示例的函數很簡單,只是一個 print 功能而已:
typedef struct _test_struct { int integer; char * c_str; void * ptr; int array[8]; } TestStruct_st; extern "C" const char *print_test_struct(TestStruct_st *pTestSt) { if (NULL == pTestSt) { return "C -- parameter NULL"; # "C --" 打頭區分這是在 .so 里面輸出的 } printf("C -- { "); printf("C -- integer : %d ", pTestSt->integer); printf("C -- cstr : %s ", pTestSt->c_str); printf("C -- ptr : %p ", pTestSt->ptr); printf("C -- array : ["); for (int tmp = 0; tmp < 7; tmp ++) { printf("%d, ", pTestSt->array[tmp]); } printf("%d] ", pTestSt->array[7]); printf("C -- } "); return "success"; }
首先,我們要對結構體進行轉換:
from ctypes import * INTARRAY8 = c_int * 8 class PyTestStruct(Structure): "TestStruct_st 的 Python 版本" _fields_ = [ ("integer", c_int), ("c_str", c_char_p), ("ptr", c_void_p), ("array", INTARRAY8) ]
首先對結構體里的 int 數組進行了重定義,也就是 INTARRAY8。
接著,注意一下 _fields_ 的內容:這里就是對 C 數據類型的轉換。左邊是 C 的結構成員名稱,右邊則是在 python 中聲明一下各個成員的類型。其他的一些類型請參見官方文檔。
此外還需要注意一下類似于 c_int, c_void_p 等等的定義是在 ctypes 中的,如果是用 impoer ctypes 的方式包含 ctypes 模塊,則應該寫成 ctypes.c_int, ctypes.c_void_p。
第三個要注意的是:這個類必須定義為 ctypes.Structure 的子類,否則在進行后續的函數傳遞時,ctypes 由于不知道如何進行數據類型的對應,會拋出異常
封裝 .so 函數class testdll: "用于 libtest.so 的加載,包含了 cdll 對象" def __init__(self): self.cdll = cdll.LoadLibrary("./libtest.so") # 直接加載 .so 文件。感覺更好的方式是寫成單例 return def print_test_struct(self, test_struct): func = self.cdll.print_test_struct func.restype = c_char_p func.argtypes = [POINTER(PyTestStruct)] return func(byref(test_struct)).decode()
注意最后一句 func(byref(test_struct)) 中的 byref。這個函數可以當作是 C 中的取地址符 & 的 Python 適配。因為函數參數是一個結構體指針(地址),因此我們需要用上 byref 函數。
Python 調用直接上 Python 代碼,很短的(import 語句就不用寫了吧,讀者自行發揮就好):
test_struct = PyTestStruct() test_struct.integer = 1 test_struct.c_str = "Hello, C".encode() # Python 2.x 則不需要寫 encode test_struct.ptr = 0xFFFFFFFF test_struct.array = INTARRAY8() for i in range(0, len(test_struct.array)): j = i + 1 test_struct.array[i] = j * 10 + j so_file = testdll() test_result = so_file.print_test_struct(test_struct) print("test_result:", test_result)
執行結果:
C -- { C -- integer : 1 C -- cstr : Hello, C C -- ptr : 0xffffffff C -- array : [11, 22, 33, 44, 55, 66, 77, 88] C -- } test_result: success
這里可以看到,結構體參數的準備還是很簡單的,就是將用 Python 適配過來之后的類中對應名字的成員進行賦值就好了。
注意一下在 Python 3.x 中,str 和 bytes 類型是區分開的,而 char * 對應的是后者,因此需要進行 encode / decode 轉換。在 Python 2.x 則不需要。
調用以回調函數地址為參數的函數這個主題就稍微繞一些了,也就是說在 C 接口中,需要傳入回調函數作為參數。這個問題在 Python 中也可以解決,并且回調函數可以用 Python 定義。
C 代碼C 代碼很簡單:回調函數的傳入參數為 int,返回參數也是 int。C 代碼獲取一個隨機數交給回調去處理。
extern "C" void print_given_num(int (*callback)(int)) { if (NULL == callback) { printf("C -- No number given "); } static int s_isInit = 0; if (0 == s_isInit) { s_isInit = 1; srand(time(NULL)); } int num = callback((int)rand()); printf("C -- given num by callback: %d (0x%x) ", num, num); return; }Python 封裝
這里我還是用前面的 testdll 類來封裝:
class testdll: "用于 libtest.so 的加載,包含了 cdll 對象" def __init__(self): self.cdll = cdll.LoadLibrary("./libtest.so") return def print_given_num(self, callback): self.cdll.print_given_num(callback) return testCallbackType = CFUNCTYPE(None, c_int, c_int)
最后的 testCallbackType 通過 ctypes 定義了一個回調函數類型,這個在后面的調用中需要使用
在 CFUNCTYPE 后面的第一個參數為 None,這表示回調函數的返回值類型為 void
Python 調用 回調函數準備回調函數用 Python 完成,注意接受的參數和返回數據類型都應該與 .so 中的定義一致。我這里的回調函數中,將 .so 傳過來的參數取了一個最低字節返回:
def _callback(para): print("get callback req:", hex(para)) print("return:", hex(para & 0xFF)) return para & 0xFF函數調用
so_file = testdll() cb = testCallbackType(_callback) so_file.print_given_num(cb)
執行結果:
get callback req: 0x4f770b3a return: 0x3a C -- given num by callback: 58 (0x3a)
怎么樣,是不是覺得很簡單?
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41427.html
摘要:指針和引用假設動態庫中有函數如下第二個參數為結構體指針,第三個參數是一個引用。我這里選擇的是然后找到,下載替換掉重編譯和輸入版本號,這里實用的是為或者參考資料通過在中調用動態鏈接庫文件厚顏無恥加上自己的博客 0x01. 使用的 npm 包 首先要安裝 node-gyp, 用來重新編譯依賴包。 npm instal -g node-gyp 然后主要用到下面三個包: node-ffi -...
摘要:由設計,作為編程語言的繼承者,于年首次發布。表達式表達式是編程語言中的語法實體,可以對其進行評估以確定其值。它是編程語言解釋和計算以產生值的常量變量函數和運算符的組合。它在年年年和年被評為年度編程語言,是唯一四次獲得該獎項的語言。 ...
摘要:最近了解了提供的一個外部函數庫它提供了語言兼容的幾種數據類型,并且可以允許調用編譯好的庫。這里是閱讀相關資料的一個記錄,內容大部分來自官方文檔。注意,提供的接口會在不同系統上有出入,比如為了加載動態鏈接庫,在上提供的是而在上提供的是和。 參考資料 https://docs.python.org/2.7/l... http://www.ibm.com/developerw... c...
閱讀 1231·2021-11-11 16:54
閱讀 881·2021-10-19 11:44
閱讀 1344·2021-09-22 15:18
閱讀 2453·2019-08-29 16:26
閱讀 2956·2019-08-29 13:57
閱讀 3099·2019-08-26 13:32
閱讀 1087·2019-08-26 11:58
閱讀 2337·2019-08-26 10:37