Python调用C/C++动态库接口的说明

简要说明

Python调用C动态库接口其实并不复杂,鉴于网上完整示例比较少,因此记录一下,这里不展开内部细节。

编译动态库

根据需求定义好相关接口,首先看一下函数声明的写法(C接口)例如:
DLL_PUBLIC int32_t getData(const CustomStructA *inParamA, const CustomStructB *inParamB, double inParamC, CustomStructA *outParamC );
这里声明了getData函数,两个入参,一个输出参数。在Python端调用的时候,Python端的实例会根据struct的内存转换成CustomStructA.例如这里将CustomStructA定义为:

struct CustomStructA
{
    uint32_t  size = 0;
    uint32_t  *buffer = nullptr;
};

struct CustomStructB
{
    uint32_t  memberA = 0;
    uint32_t  memberB = 0;
};

通过这个函数调用的例子说明如何传入数组,输出数组,内存管理问题。

#ifdef WIN32
#define DLL_PUBLIC _declspec(dllexport)
#else
#define DLL_PUBLIC __attribute ((visibility("default")))
#endif
DLL_PUBLIC int32_t getData(const CustomStructA *inParamA, const CustomStructB *inParamB, double inParamC, CustomStructA *outParamC )
{
    if (nullptr == inParamA || nullptr == inParamB || nullptr == outParamC)
    {
        return -1;
    }
    printf("CustomStructA buffer size %d\n", inParamA->size);
    for (uint32_t i = 0; i < inParamA->size; i ++)
    {
        printf("CustomStructA buffer buffer[%d] %d\n", i,  inParamA->buffer[i]);
    }
    printf("CustomStructB memberA %d\n", inParamB->memberA);
    printf("CustomStructB memberB %d\n", inParamB->memberB);
    printf("inParamC %lf\n", inParamC);
    outParamC->size = 9;
    outParamC->buffer = new uint32_t[outParamC->size];
    for (uint32_t m = 0; m < outParamC->size; m ++)
    {
        outParamC->buffer[m] = m;
    }
    return 0;
}
DLL_PUBLIC void releaseCustomStructA(CustomStructA *inParam)
{
	if (nullptr != inParam && nullptr != inParam->buffer)
		inParam->size = 0;
    	delete[] (inParam->buffer);
}

编译后会得到test.dll或者test.so

调用动态库

首先根据接口参数定义对应的Python端的对象:

from ctypes import *
class PyCustomStructA(Structure):
    _fields_ = [("size", c_uint32), 
                ("buffer", POINTER(c_uint32))]

class PyCustomStructB(Structure):
    _fields_ = [("member_a", c_uint32), 
                ("member_b", c_uint32)]

    def __init__(self, val_a, val_b):
        self.member_a = val_a
        self.member_b = val_b
dllName = "../lib/Release/test.so" 
dllHandle = cdll.LoadLibrary(dllName)
 c_param_a = PyCustomStructA()
 c_param_a.size = 6
 input_buffer = c_uint32 * c_param_a.size
 c_param_a.buffer = input_buffer()
 c_param_a.buffer[0] = 2
 c_param_a.buffer[1] = 0
 c_param_a.buffer[2] = 2
 c_param_a.buffer[3] = 1
 c_param_a.buffer[4] = 0
 c_param_a.buffer[5] = 3
 c_param_b = PyCustomStructB(16, 39)
 c_param_c = PyCustomStructA()
 var_ret = dllHandle.getData(byref(c_param_a), byref(c_param_b), c_double(5.58), byref(c_param_c))
 if 0 == var_ret:
     for val_index in range(0, c_param_c.size):
         print("c_param_c value %d" % c_param_c.buffer[val_index])
dllHandle.releaseCustomStructA(byref(c_param_c))

在类的定义中,其中_fields_说明具体成员,成员的具体类型形式为(“size”, c_uint32),除了是基础数据类型,还可以是自定义的类型如(“member_c”, PyCustomStructB)、(“member_c”, POINTER(PyCustomStructB))等等。
通过动态库的路径,调用cdll.LoadLibrary得到句柄;dllHandle加”.“加”接口函数名称“,可以直接调用该函数。
在参数中,指针可以使用byref函数得到Python端对象的地址,Python端传入的数值对象需要通过c_double()/c_uint64()等方式转换后才能作为double/uint64_t的值进行函数调用。
Python端传入数组时,可以通过以下方式:
input_buffer = c_uint32 * c_param_a.size
c_param_a.buffer = input_buffer()
构建数组
这里示例代码的调试输出结果为:
动态库:
CustomStructA buffer size 6
CustomStructA buffer buffer[0] 2
CustomStructA buffer buffer[1] 0
CustomStructA buffer buffer[2] 2
CustomStructA buffer buffer[3] 1
CustomStructA buffer buffer[4] 0
CustomStructA buffer buffer[5] 3
CustomStructB memberA 16
CustomStructB memberB 39
inParamC 5.580000

Python端:
c_param_c value 0
c_param_c value 1
c_param_c value 2
c_param_c value 3
c_param_c value 4
c_param_c value 5
c_param_c value 6
c_param_c value 7
c_param_c value 8

内存释放

从动态库中申请的内存需要主动释放:
dllHandle.releaseCustomStructA(byref(c_param_c))
c_param_c在释放后,这个实例就不要再继续使用了

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值