Il existe plusieurs façons d'appeler une fonction c depuis python, mais cette fois, je vais vous expliquer comment utiliser les ctypes. J'ai utilisé Windows comme environnement d'exécution.
La procédure générale est la suivante.
Les DLL sont créées à l'aide de Visual Studio.
Cette fois, j'ai utilisé Visual Studio 2019.
URL de référence https://docs.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=vs-2019
ctypes_sample1.h
#pragma once
#ifdef CTYPES_SAMPLE1_EXPORTS
#define CTYPES_SAMPLE1_API __declspec(dllexport)
#else
#define CTYPES_SAMPLE1_API __declspec(dllimport)
#endif
struct Vector3D
{
int x;
int y;
int z;
};
extern "C" CTYPES_SAMPLE1_API double add_double(double a, double b);
extern "C" CTYPES_SAMPLE1_API int add_int(int a, int b);
extern "C" CTYPES_SAMPLE1_API double accumulate(const double *x, int len);
extern "C" CTYPES_SAMPLE1_API bool copy(const double *from, double *to, int n);
extern "C" CTYPES_SAMPLE1_API bool copy2d(const double **from, double ** to, int m, int n);
extern "C" CTYPES_SAMPLE1_API Vector3D AddVector(Vector3D a, Vector3D b);
ctypes_sample1.cpp
#include"pch.h"
#include "ctypes_sample1.h"
double add_double(double a, double b)
{
return a + b;
}
int add_int(int a, int b)
{
return a + b;
}
double accumulate(const double* x, int len)
{
double sum = 0;
for (int i = 0; i < len; i++)
{
sum += x[i];
}
return sum;
}
bool copy(const double* from, double* to, int n)
{
for (int i = 0; i < n; i++) {
to[i] = from[i];
}
return true;
}
bool copy2d(const double** from, double** to, int m, int n)
{
for (int j = 0; j < n; j++) {
for (int i = 0; i < m; i++) {
to[j][i] = from[j][i];
}
}
return true;
}
Vector3D AddVector(Vector3D a, Vector3D b)
{
Vector3D v;
v.x = a.x + b.x;
v.y = a.y + b.y;
v.z = a.z + b.z;
return v;
}
Placez la DLL créée directement sous .py.
Pour appeler une fonction, suivez la procédure ci-dessous.
URL de référence: https://docs.python.org/3/library/ctypes.html
add_int
import ctypes
mydll=ctypes.cdll.LoadLibrary('ctypes_sample1')
addi=mydll.add_int
addi.argtypes=(ctypes.c_int,ctypes.c_int)
addi.restype=ctypes.c_int
addi(1,2)
3
add_doulbe
import ctypes
mydll=ctypes.cdll.LoadLibrary('ctypes_sample1')
addd=mydll.add_double
addd.restype=ctypes.c_double
addd.argtypes=(ctypes.c_double,ctypes.c_double)
addd(1.1,2.1)
3.2
Vous pouvez passer un tableau en tant qu'argument en utilisant un tableau de ctypes ou en utilisant numpy.
ctypes array
Lors de la création d'un tableau avec ctypes, spécifiez la taille du tableau avec *.
c_int * 5
Si vous voulez plus de pointeurs de tableau, utilisez ctypes.pointer ().
Pour indiquer que l'argument est un pointeur, utilisez POINTER supérieur, tel que ctypes.POINTER (ctypes.c_doulbe).
import ctypes
mydll=ctypes.cdll.LoadLibrary('ctypes_sample1')
accumulate = mydll.accumulate
accumulate.restype=ctypes.c_double
accumulate.argtypes=(ctypes.POINTER(ctypes.c_double),ctypes.c_int)
_arr1=[1.0,2.0,3.0,4.0]
arr1 = (ctypes.c_double*len(_arr1))(*_arr1)
accumulate(cast(pointer(arr1),ctypes.POINTER(ctypes.c_double)),len(arr1))
10.0
import ctypes
mydll=ctypes.cdll.LoadLibrary('ctypes_sample1')
copy1d=mydll.copy
copy1d.argtypes=(ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_double),ctypes.c_int)
copy1d.restype=ctypes.c_bool
arr1 = (ctypes.c_double*10)(*range(10))
arr2 = (ctypes.c_double*10)(*([0]*10))
copy1d(cast(pointer(arr1),POINTER(ctypes.c_double)),cast(pointer(arr2),POINTER(ctypes.c_double)),len(arr1))
for i in range(len(arr1)):
print('arr1[{0:d}]={1:.1f} arr2[{0:d}]={2:.1f}'.format(i,arr1[i],arr2[i]))
arr1[0]=0.0 arr2[0]=0.0 arr1[1]=1.0 arr2[1]=1.0 arr1[2]=2.0 arr2[2]=2.0 arr1[3]=3.0 arr2[3]=3.0 arr1[4]=4.0 arr2[4]=4.0 arr1[5]=5.0 arr2[5]=5.0 arr1[6]=6.0 arr2[6]=6.0 arr1[7]=7.0 arr2[7]=7.0 arr1[8]=8.0 arr2[8]=8.0 arr1[9]=9.0 arr2[9]=9.0
numpy array
Si vous voulez passer des données numpy comme argument, utilisez ctypes.data_as () pour obtenir un pointeur vers l'objet ctypes et le passer comme argument.
URL de référence: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.ctypes.html
import ctypes
import numpy as np
mydll=ctypes.cdll.LoadLibrary('ctypes_sample1')
accumulate = mydll.accumulate
accumulate.restype=ctypes.c_double
accumulate.argtypes=(ctypes.POINTER(ctypes.c_double),ctypes.c_int)
arr1=np.array([1.0,2.0,3.0,4.0],dtype=np.float)
accumulate(arr1.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),len(arr1))
10.0
import ctypes
import numpy as np
mydll=ctypes.cdll.LoadLibrary('ctypes_sample1')
copy1d=mydll.copy
copy1d.argtypes=(ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_double),ctypes.c_int)
copy1d.restype=ctypes.c_bool
arr1=np.random.rand(10)
arr2=np.zeros_like(arr1)
copy1d(arr1.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),arr2.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),len(arr1))
for i in range(len(arr1)):
print('arr1[{0:d}]={1:f} arr2[{0:d}]={2:f}'.format(i,arr1[i],arr2[i]))
arr1[0]=0.062427 arr2[0]=0.062427 arr1[1]=0.957770 arr2[1]=0.957770 arr1[2]=0.450949 arr2[2]=0.450949 arr1[3]=0.609982 arr2[3]=0.609982 arr1[4]=0.351959 arr2[4]=0.351959 arr1[5]=0.300281 arr2[5]=0.300281 arr1[6]=0.148543 arr2[6]=0.148543 arr1[7]=0.094616 arr2[7]=0.094616 arr1[8]=0.379529 arr2[8]=0.379529 arr1[9]=0.810064 arr2[9]=0.810064
ctypes array
Lorsque vous passez un tableau à deux dimensions, passez un tableau de pointeurs.
import ctypes
mydll=ctypes.cdll.LoadLibrary('ctypes_sample1')
copy2d = mydll.copy2d
copy2d.argtypes=((ctypes.POINTER(ctypes.c_double*5)*2),(ctypes.POINTER(ctypes.c_double*5)*2),ctypes.c_int,ctypes.c_int)
copy2d.restype=ctypes.c_bool
_arr1 = [ [1,2,3,4,5], [6,7,8,9,10] ]
arr1=((ctypes.c_double*5)*2)()
for j in range(2):
for i in range(5):
arr1[j][i] = _arr1[j][i]
arr2=((ctypes.c_double*5)*2)()
app1p=(ctypes.POINTER(ctypes.c_double*5)*2)()
for j in range(2):
app1p[j] = ctypes.pointer(arr1[j])
app2p=(ctypes.POINTER(ctypes.c_double*5)*2)()
for j in range(2):
app2p[j] = ctypes.pointer(arr2[j])
copy2d(app1p,app2p,5,2)
for j in range(2):
for i in range(5):
print('arr1[{0:d},{1:d}]={2:f} arr2[{0:d},{1:d}]={3:f}'.format(j,i,arr1[j][i],arr2[j][i]))
arr1[0,0]=1.000000 arr2[0,0]=1.000000 arr1[0,1]=2.000000 arr2[0,1]=2.000000 arr1[0,2]=3.000000 arr2[0,2]=3.000000 arr1[0,3]=4.000000 arr2[0,3]=4.000000 arr1[0,4]=5.000000 arr2[0,4]=5.000000 arr1[1,0]=6.000000 arr2[1,0]=6.000000 arr1[1,1]=7.000000 arr2[1,1]=7.000000 arr1[1,2]=8.000000 arr2[1,2]=8.000000 arr1[1,3]=9.000000 arr2[1,3]=9.000000 arr1[1,4]=10.000000 arr2[1,4]=10.000000
numpy array
Aussi pour numpy, passez un tableau de pointeurs. Le type du tableau de pointeurs est créé à l'aide de numpy.ctypeslib.ndpointer. Puisque dtype est un pointeur, spécifiez uintp. arr1. \ __array_interface \ __ ['data'] [0] 'récupère le premier pointeur. Obtient le nombre d'octets lorsqu'une ligne est déplacée avec arr1.strides [0].
import ctypes
import numpy as np
mydll=ctypes.cdll.LoadLibrary('ctypes_sample1')
copy2d = mydll.copy2d
_doublepp = np.ctypeslib.ndpointer(dtype=np.uintp, ndim=1, flags='C')
copy2d.argtypes=(_doublepp,_doublepp,ctypes.c_int,ctypes.c_int)
copy2d.restype=ctypes.c_bool
arr1=np.random.rand(4*7).reshape(4,7)
arr2=np.zeros_like(arr1)
arr1pp = (arr1.__array_interface__['data'][0]
+ np.arange(arr1.shape[0])*arr1.strides[0]).astype(np.uintp)
arr2pp = (arr2.__array_interface__['data'][0]
+ np.arange(arr2.shape[0])*arr2.strides[0]).astype(np.uintp)
copy2d(arr1pp,arr2pp,arr1.shape[1],arr2.shape[0])
for j in range(arr2.shape[0]):
for i in range(arr2.shape[1]):
print('arr1[{0:d},{1:d}]={2:f} arr2[{0:d},{1:d}]={3:f}'.format(j,i,arr1[j,i],arr2[j,i]))
arr1[0,0]=0.301684 arr2[0,0]=0.301684 arr1[0,1]=0.423735 arr2[0,1]=0.423735 arr1[0,2]=0.761370 arr2[0,2]=0.761370 arr1[0,3]=0.990014 arr2[0,3]=0.990014 arr1[0,4]=0.547852 arr2[0,4]=0.547852 arr1[0,5]=0.773549 arr2[0,5]=0.773549 arr1[0,6]=0.695525 arr2[0,6]=0.695525 arr1[1,0]=0.156089 arr2[1,0]=0.156089 arr1[1,1]=0.619667 arr2[1,1]=0.619667 arr1[1,2]=0.602623 arr2[1,2]=0.602623 arr1[1,3]=0.555263 arr2[1,3]=0.555263 arr1[1,4]=0.670706 arr2[1,4]=0.670706 arr1[1,5]=0.483012 arr2[1,5]=0.483012 arr1[1,6]=0.318976 arr2[1,6]=0.318976 arr1[2,0]=0.336153 arr2[2,0]=0.336153 arr1[2,1]=0.518378 arr2[2,1]=0.518378 arr1[2,2]=0.440815 arr2[2,2]=0.440815 arr1[2,3]=0.165265 arr2[2,3]=0.165265 arr1[2,4]=0.370611 arr2[2,4]=0.370611 arr1[2,5]=0.249356 arr2[2,5]=0.249356 arr1[2,6]=0.798799 arr2[2,6]=0.798799 arr1[3,0]=0.216579 arr2[3,0]=0.216579 arr1[3,1]=0.028188 arr2[3,1]=0.028188 arr1[3,2]=0.529525 arr2[3,2]=0.529525 arr1[3,3]=0.381811 arr2[3,3]=0.381811 arr1[3,4]=0.495189 arr2[3,4]=0.495189 arr1[3,5]=0.339180 arr2[3,5]=0.339180 arr1[3,6]=0.131087 arr2[3,6]=0.131087
Utilisez ctypes.Structure pour transmettre la structure. Spécifiez le type de variable membre dans \ _fields \ _.
import ctypes
mydll=ctypes.cdll.LoadLibrary('ctypes_sample1')
class VECTOR3D(ctypes.Structure):
_fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int), ("z", ctypes.c_int)]
vector_a = VECTOR3D(1, 2, 3)
vector_b = VECTOR3D(2, 3, 4)
AddVector = mydll.AddVector
AddVector.argtypes=(VECTOR3D,VECTOR3D)
AddVector.restype = VECTOR3D
vector_c = AddVector(vector_a, vector_b)
print('x={},y={},z={}'.format(vector_c.x,vector_c.y,vector_c.z))
x=3,y=5,z=7