Python 調用動態鏈接庫

Python 調用動態鏈接庫

ctypesPython調用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.txtcmake 的配置文件。相關詳細配置請參考官方文檔。

動態鏈接庫頭文件 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.soLinux系統生成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樣例代碼,請留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章