Python
調用動態鏈接庫
ctypes
是Python
調用c
的動態鏈接庫的一個內置模塊。
通過 CMake
構建動態鏈接庫
項目結構
├── CMakeLists.txt # CMake 構建配置文件
├── library.h # 動態鏈接庫頭文件
└── library.cpp # 動態鏈接庫源文件
CMakeLists.txt
配置內容如下:
cmake_minimum_required(VERSION 3.10)
project(excapp)
set(CMAKE_CXX_STANDARD 11)
set(EXCAPPLIB library.cpp library.h)
add_library(excapp SHARED ${EXCAPPLIB})
CMakeLists.txt
是cmake
的配置文件。相關詳細配置請參考官方文檔。
動態鏈接庫頭文件 library.h
內容如下:
#ifdef __cplusplus
extern "C" {
#endif
#ifndef EXCAPP_LIBRARY_H
#define EXCAPP_LIBRARY_H
#include <string>
typedef void (*FUNP)();
typedef void (*FUNP1)(char *ch);
void hello();
void echo(char *text);
void printUser(char *user, unsigned int age);
char *getName(char *userName);
int sum(int a, int b);
void *getVoidData(void *data);
void *getVoidData1(void *data, int *addr);
void callback(FUNP funp);
void callback1(FUNP1 funp1, char *text);
#endif
#ifdef __cplusplus
}
#endif
動態鏈接庫源文件 library.cpp
內容如下:
#include <string>
#include "library.h"
typedef void (*FUNP)();
typedef void (*FUNP1)(char *ch);
void hello() {
printf("%s\n", "Hello, World!");
}
void echo(char *text) {
printf("%s\n", text);
}
void printUser(char *user, unsigned int age) {
printf("His name is %s and is %d years old.\n", user, age);
}
char *getName(char *userName) {
return userName;
}
int sum(int a, int b) {
return a + b;
}
void *getVoidData(void *data) {
return data;
}
void *getVoidData1(void *data, int *addr) {
char *tmp = static_cast<char *>(data);
*addr = strlen(tmp);
return data;
}
void callback(FUNP funp) {
(*funp)();
}
void callback1(FUNP1 funp1, char *text) {
(*funp1)(text);
}
構建編譯,命令如下:
$ cmake .
$ make
注:上述命令是在
Ubuntu
系統下執行的。如果是其他操作系統平臺,請自行適配相應平臺操作方法。本人平臺是Ubuntu
,所以生成的動態鏈接庫名稱爲libexcapp.so
。Linux
系統生成libexcapp.so
文件,Windows
系統生成excapp.dll
文件。
注意:Python
不支持調用C++
編譯生成的動態鏈接庫。需要添加如下內容:
#ifdef __cplusplus
extern "C" {
#endif
// 您的邏輯內容在這
#ifdef __cplusplus
}
#endif
加載動態鏈接庫
下面代碼是最簡單的加載動態鏈接庫
import ctypes
lib = ctypes.CDLL("libexcapp.so")
更嚴謹的加載方式是判斷當前運行平臺,並根據當前平臺加載相應的動態鏈接庫。
import platform
if 'linux' in str(platform.system()).lower():
lib = ctypes.CDLL("libexcapp.so")
elif 'windows' in str(platform.system()).lower():
lib = ctypes.WinDLL("excapp.dll")
elif 'darwin' == str(platform.system()).lower():
lib = ctypes.CDLL("libexcapp.dylib")
調用庫的接口
簡單的調用接口。
lib.hello()
調用帶參數的接口
調用帶有一個參數爲 char *
類型的接口:
lib.echo("This is echo method.".encode("utf8"))
更嚴謹的調用方式是指定參數列表中各個參數的類型,代碼如下:
lib.echo.argtype=ctypes.c_char_p
lib.echo("This is echo method.".encode("utf8"))
調用具有多個參數的接口
調用接口需要傳遞多個參數的接口:
lib.printUser.argtypes = (ctypes.c_char_p, ctypes.c_uint)
lib.printUser("小寶".encode("utf-8"), 12)
調用基友多個參數並有返回值的接口
注:指定參數類型,並且指定返回類型
樣例1:
lib.getName.argtype = ctypes.c_char_p
lib.getName.restype = ctypes.c_char_p
res = lib.getName("小寶".encode("utf-8"))
print(res)
print(type(res))
print(res.decode("utf-8"))
樣例2:
res = lib.sum(1, 2)
print(res)
調用void *
參數或返回類型的接口
樣例1:
返回數據類型爲 void *
接口,在 Python
中需要使用 ctypes.cast(obj,type)
強制轉相關類型。
lib.getVoidData.argtype = ctypes.c_void_p
lib.getVoidData.restype = ctypes.c_void_p
res = lib.getVoidData("中國".encode("utf-8"))
ccharp = ctypes.cast(res, ctypes.c_char_p)
print(res)
print(ccharp)
print(ccharp.value)
print(ccharp.value.decode("utf-8"))
樣例2:
該樣例的接口有指針類型參數,並且返回 void *
的數據接口。指針的傳遞需要 ctypes.byref()
傳入接口。返回的 void *
數據類型,通過 ctypes.string_at(obj,int)
獲取指定長度的數據流,這個在流處理的時候非常重要,比如獲取音頻數據流。
lib.getVoidData1.argtypes = (ctypes.c_char_p, ctypes.POINTER(ctypes.c_int))
lib.getVoidData1.restype = ctypes.c_void_p
lenth = ctypes.c_int(0)
res = lib.getVoidData1("您好呀".encode("utf-8"), ctypes.byref(lenth))
data = ctypes.string_at(res, int(lenth.value))
print(res)
print(data)
print(lenth)
調用有回調函數的接口
樣例1:
調用有回調函數的接口,通過 ctypes.CFUNCTYPE()
指定回調函數的返回類型和參數列表中各個參數的類型。即使沒有任何的返回值,也需要指定返回類型爲 None
。
def callback():
print("您好呀")
callback_type = ctypes.CFUNCTYPE(None)
lib.callback.argtype = callback_type
cal = callback_type(callback)
lib.callback(cal)
樣例2:
調用有回調函數的接口,指定參數列表的參數類型。
def callback(text):
print(text)
callback_type = ctypes.CFUNCTYPE(None, ctypes.c_char_p)
lib.callback.argtypes = (callback_type, ctypes.c_char_p)
cal = callback_type(callback)
lib.callback1(cal, "你好呀".encode("utf-8"))
注意
在 Python3
中,ctypes
傳遞的數據類型都是 byte
流。
如果需要demo樣例代碼,請留言。