python使用ctypes调用gcc编译的dll之ctypes的使用
简介
ctypes 是 Python 的外部函数库。它提供了与 C 兼容的数据类型,并允许调用C或C++编译后的DLL 或共享库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。
本例中代码基于window系统,python为64位3.9.12,如需在liunx上使用请参考上篇博客
1、代码的基础结构如下
1.1 新建header.h代码如下
#pragma once
#define DllExport __declspec( dllexport )
struct Point{
int x;
double y;
};
extern "C"
{
DllExport void hello_world(void);
DllExport char print_chr(char chr);
DllExport double add(int num1, float num2, double num3);
DllExport char* test_chrp(char *s);
DllExport int test_intp(int arr1[], int arr1_len, int arr2[][2], int arr2_len);
DllExport double* test_dabp(int *p1, float *p2, double *p3);
DllExport void print_point(struct Point point);
}
1.2 新建test.c代码如下
#include <iostream>
#include <string.h>
//#include <stdio.h>
#include "header.h"
//void hello_world() {
// printf("hello world");
//}
void hello_world() {
std::cout << "hello world" << std::endl;
}
char print_chr(char chr){
std::cout << chr << std::endl;
return chr;
}
double add(int num1, float num2, double num3){
return num1 + num2 + num3;
}
char* test_chrp(char *s) {
static char hel[] = "hello";
char *p;
int i;
p = hel;
for (i = 0; i < 2; i++)
{
*(s + i) = hel[i];
}
return p;
}
double* test_dabp(int *p1, float *p2, double *p3){
// C 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量
static double da[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};
double *p;
int i;
da[0] = da[0] + *p1 + *p2 + *p3;
p = da;
return p;
}
int test_intp(int arr1[], int arr1_len, int arr2[][2], int arr2_len){
int i;
for ( i = 0; i < arr1_len; i++ )
{
arr1[i] = arr1[i] + 10;
// std::cout << "arr1[" << i << "]: ";
// std::cout << arr1[i]<< std::endl;
}
int k, j;
for ( int k = 0; k < arr2_len; k++ ){
for ( int j = 0; j < 2; j++ )
{
arr2[k][j] = arr2[k][j] + 100;
// std::cout << "arr2[" << k << "][" << j << "]: ";
// std::cout << arr2[k][j]<< std::endl;
}
}
return 0;
}
void print_point(struct Point point){
std::cout << "x: "<< point.x << ", y: "<< point.y << std::endl;
}
1.3 编译C++代码
g++ test.cpp -fPIC -static -shared -o test.dll
1.4 新建test.py代码如下
# -*- coding: UTF-8 -*-
# import ctypes
from ctypes import *
# test = cdll.LoadLibrary('./test.dll')
test = CDLL('./test.dll')
test.hello_world()
# C/C++常用的基本数据类型作为函数的参数的使用
# 以char类型作为参数和返回值
test.print_chr.restype = c_char
chr_res = test.print_chr(c_char(b'Y'))
print("chr_res: ", chr_res)
# 以int, float, double类型作为参数并返相加后的double类型结果
test.add.restype = c_double
test.add.argtypes = [c_int, c_float, c_double]
int_res = test.add(c_int(5), c_float(0.5), c_double(0.05))
print("double_res: ", int_res)
# 以char指针作为参数,并返回一个char指针
test.test_chrp.restype = c_char_p
test.test_chrp.argtypes = [c_char_p]
# cp = cast((c_char * 2)(), c_char_p)
cp = c_char_p(b"s")
res = test.test_chrp(cp)
print(res)
print(cp.value)
# 以int, float, double类型的指针作为参数并返相加后的double的指针类型结果
test.test_dabp.restype = POINTER(c_double)
test.test_dabp.argtypes = [POINTER(c_int), POINTER(c_float), POINTER(c_double)]
res = test.test_dabp(pointer(c_int(100)), pointer(c_float(0.1)), pointer(c_double(0.01)))
# print(res)
# print(res[1])
for i in range(5):
print(res[i])
# 用int类型的一维数组和二维数组作为函数参数, 然后将arr1的每个元素加10, arr2的每个元素加100,并打印数组元素的值
# 第一种方式使用数组作为传参的参数
test.test_intp.argtypes = [c_int * 5, c_int, (c_int * 2)*5, c_int]
arr1 = (c_int * 5)(1, 2, 3, 4, 5)
arr2 = ((c_int * 2)*5)((1, 2), (3, 4), (5, 6), (7, 8), (9, 10))
res = test.test_intp(arr1, 5, arr2, 5)
for i in range(5):
print("python arr1:", arr1[i])
for i in range(5):
for j in range(2):
print("python arr2:", arr2[i][j])
# 第二种方式使用指针作为传参的参数
test.test_intp.argtypes = [POINTER(c_int), c_int, POINTER(c_int), c_int]
arr1 = (c_int * 5)(1, 2, 3, 4, 5)
arr1 = cast(arr1, POINTER(c_int))
arr2 = ((c_int * 2)*5)((1, 2), (3, 4), (5, 6), (7, 8), (9, 10))
arr2 = cast(arr2, POINTER(c_int))
res = test.test_intp(arr1, 5, arr2, 5)
for i in range(5):
print("python arr1:", arr1[i])
for i in range(10):
print("python arr2:", arr2[i])
# 使用c语言中的结构体
class Point(Structure):
_fields_ = (
('x', c_int),
('y', c_double),
)
point = Point(1, 2)
print(point.x, point.y)
test.print_point(point)
print(point.x, point.y)
1.5 运行python代码
python test.py