Es gibt verschiedene Möglichkeiten, eine c-Funktion von Python aus aufzurufen, aber dieses Mal werde ich erklären, wie ctypes verwendet wird. Ich habe Windows als Ausführungsumgebung verwendet.
Das Gesamtverfahren ist wie folgt.
DLLs werden mit Visual Studio erstellt.
Diesmal habe ich Visual Studio 2019 verwendet.
Referenz-URL 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;
}
Platzieren Sie die erstellte DLL direkt unter .py.
Gehen Sie wie folgt vor, um eine Funktion aufzurufen.
Referenz-URL: 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
Sie können ein Array als Argument übergeben, indem Sie ein Array von ctypes oder numpy verwenden.
ctypes array
Geben Sie beim Erstellen eines Arrays mit ctypes die Größe des Arrays mit * an.
c_int * 5
Wenn Sie mehr Array-Zeiger wünschen, verwenden Sie ctypes.pointer ().
Verwenden Sie den oberen Zeiger, z. B. ctypes.POINTER (ctypes.c_doulbe), um anzuzeigen, dass das Argument ein Zeiger ist.
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
Wenn Sie numpy-Daten als Argument übergeben möchten, verwenden Sie ctypes.data_as (), um einen Zeiger auf das ctypes-Objekt abzurufen und als Argument zu übergeben.
Referenz-URL: 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
Übergeben Sie beim Übergeben eines zweidimensionalen Arrays ein Array von Zeigern.
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
Übergeben Sie auch für numpy ein Array von Zeigern. Der Typ des Zeigerarrays wird mit numpy.ctypeslib.ndpointer erstellt. Da dtype ein Zeiger ist, geben Sie uintp an. arr1. \ __array_interface \ __ ['data'] [0] 'holt den ersten Zeiger. Ruft die Anzahl der Bytes ab, wenn eine Zeile mit arr1.strides [0] verschoben wird.
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
Verwenden Sie ctypes.Structure, um die Struktur zu übergeben. Geben Sie den Typ der Mitgliedsvariablen in \ _fields \ _ an.
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
Recommended Posts