Python 调用动态链接库

Python 调用动态链接库Python调用动态链接库ctypes是Python调用c的动态链接库的一个内置模块。通过CMake构建动态链接库项目结构├──CMakeLists.txt#CMake构建配置文件├──library.h#动态链接库头文件└──library.cpp#动态链接库源文件…

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样例代码,请留言。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/34826.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注