python使用ctypes调用gcc编译的dll之ctypes的使用

发布时间 2023-07-27 15:08:57作者: 平平无奇小辣鸡

简介

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