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)