Python 调用动态链接库教程(return/指针/全局变量)

Python 调用动态链接库教程(return/指针/全局变量)

[TOC]

1. 前言

当下Python在汽车电子行业越来越火,尤其在各家上了AUTOSAR之后,由于其繁琐的开发流程以及相关工具尚未完善的现状,导致需要进行一系列的工具化,自动化,工具链化的工作,Python作为近几年火气来的语言,加之网络上已经存在很多汽车电子开发过程中需要的轮子(例如 canmatrix 可以一键转换dbc为excel等),导致身边的同事,以及不同公司的同行,不约而同的采用python作为主力工具开发语言。

在项目开发过程中不免存在与供应商进行产品的合作开发情况,本教程介绍的方案是基于此场景下,C语言编写了需要保密的核心算法,Python开发相应的测试程序,进行两部分的联合调用。

2. C语言实现部分

  • testDll.c
/*-----------------------------------------------------------------------------
/*   Testing file for dll and python combine
/*  
/*   File         : testDll.c
/*   Complier     : gcc  Ver. > 4.9
/*   Author       : Tomato 
/*   Time         : 2019/8/23
/*   Instructions : New file                                           - 0.1    
/*                  Add global parameter  gTestData                    - 0.2
/*                 
/*---------------------------------------------------------------------------*/

/* Incluce files */
#include "testDll.h"

/* Parameters */
uint8 gTestData = 66u;

/* Function Declation */
void __TestFunc(uint8 * arrayHead, uint8 arraySize);
uint8 __TestFuncRtn(uint8 argv1, uint8 argv2);

void main()
{
    /* */
}

/* Function start----------------------------------------------------------- */
void __TestFuncAry(uint8 * arrayHead, uint8 arraySize)
{
    for (uint8 i = 0; i < arraySize; i++)
    {
        arrayHead[i] ++;
    }
}

/* End of function---------------------------------------------------------- */

/* Function start----------------------------------------------------------- */
uint8 __TestFuncRtn(uint8 argv1, uint8 argv2)
{
    uint8 temp_vle = 0u;
    temp_vle = argv1 + argv2;
    return temp_vle;
}

/* End of function---------------------------------------------------------- */

/* Function start----------------------------------------------------------- */
uint8 GetGblData(void)
{
    return gTestData;
}

/* End of function---------------------------------------------------------- */


  • testDll.h
/*-----------------------------------------------------------------------------
/*   Testing file for dll and python combine
/*  
/*   File         : testDll.h
/*   Complier     : gcc  Ver. > 4.9
/*   Author       : Tomato
/*   Time         : 2019/8/23
/*   Instructions : New file                                           - 0.1    
/*                  Add global parameter                               - 0.2        
/*                 
/*---------------------------------------------------------------------------*/

#ifndef _TESTDLL_H
#define _TESTDLL_H

/* Alies type define */
typedef signed char int8_T;
typedef unsigned char uint8_T;
typedef short int16_T;
typedef unsigned short uint16_T;
typedef int int32_T;
typedef unsigned int uint32_T;
typedef float real32_T;
typedef double real64_T;
typedef double real_T;
typedef double time_T;
typedef unsigned char boolean_T;

/* AUTOSAR Base to Platform types mapping */
typedef boolean_T boolean;
typedef int16_T sint16;
typedef int32_T sint32;
typedef int8_T sint8;
typedef uint16_T uint16;
typedef uint32_T uint32;
typedef uint8_T uint8;
typedef real32_T float32;
typedef real_T float64;

/* Function Declation */
extern void __TestFuncAry(uint8 * arrayHead, uint8 arraySize);
extern uint8 __TestFuncRtn(uint8 argv1, uint8 argv2);
extern uint8 GetGblData(void);

/* Variable Declation*/
extern uint8 gTestData;

#endif

本示例仅存在一个.c文件,实际应用场景会存在很多源代码文件,所以最好的方式是把c编译成一个动态链接库,进行统一调用。

针对本示例采用如下命令进行动态链接库的编译(前提是电脑要安装gcc编译器)
- windows

gcc -fPIC -shared testDll.c -o testDll.dll
  • linux
gcc -fPIC -shared testDll.c -o testDll.so

若为多个源代码文件可以编写makefile。

3. Python 测试部分代码

需要注意的是,由于c和Python中数据类型并不通用,如果使用数组(地址连续,且各元素数据类型统一)等复杂数据类型需要先进行转换等前处理。

本教程使用的是numpy模块进行数据类型转换,程序示例如下:

  • Python
 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
#    Testing file for dll/so and python combine
#   
#    File         : testPyWithC.py
#    Author       : Tomato
#    Time         : 2019/8/23
#    Instructions : New file                                           - 0.1   
#                   Add global parameter access and setting            
#                                    2021/6/13   Tomato                - 0.2         
#                  
# -----------------------------------------------------------------------------

# Import files
from ctypes import *
import numpy as np

# load the shared object file ,*.so(linux) or *.dll(windows)
testLib = CDLL('./testDll.dll')

# test normal input, datatype implict truned
testRtn = testLib.__TestFuncRtn(15,20)
print(testRtn)

# test array ,should convert datatype first------------------------------------
strInput = input("Please input testing array eg.50 60: ")
# Turn string input to number
input_list = strInput.split(' ')
num_list = list()
for data in input_list:
    num_list.append(int(data))

# Judge generate array's memmroy whether is continues
# 判断使用numpy生成的uint8类型数组,在内存中地址是否连续,如果不连续则需要进行强制连续-重要!
testArray = np.asarray(num_list, dtype=np.uint8).reshape(-1)
if not testArray.flags['C_CONTIGUOUS']:
    testArray = testArray.ascontiguous(testArray, dtype=testArray.dtype)

# Turn to ctypes pointer
# 转换为ctypes指针,对应于c语言中的uint8 *
testArrayPtr = cast(testArray.ctypes.data, POINTER(c_uint8))

# Get array length
aryLen = len(num_list)

# Testing c function with pointer used
testLib.__TestFuncAry(testArrayPtr,aryLen)

for i in range(aryLen):
    print(testArrayPtr[i])

# Testing global parameter between c/python
gTestData = c_uint8.in_dll(testLib, 'gTestData')
print(gTestData)
gTestData = 77
c_uint8.in_dll(testLib, 'gTestData').value = gTestData
tp_rtn = testLib.GetGblData()
print(tp_rtn)