There are several ways to call a c function from python, but this time I will explain how to use ctypes. I used windows as the execution environment.
The overall procedure is as follows.
DLLs are created using Visual Studio.
This time I used Visual Studio 2019.
Reference 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;
}
Place the created dll directly under .py.
To call a function, follow the procedure below.
Reference 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
You can pass an array as an argument by using array of ctypes or by using numpy.
ctypes array
When creating an array with ctypes, specify the size of the array with *.
c_int * 5
If you want more array pointers, use ctypes.pointer ().
To indicate that the argument is a pointer, use an uppercase POINTER, such as 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
If you want to pass numpy data as an argument, use ctypes.data_as () to get a pointer to the ctypes object and pass it as an argument.
Reference 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
When passing a two-dimensional array, pass an array of pointers.
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
Also for numpy, pass an array of pointers. Pointer array types are created using numpy.ctypeslib.ndpointer. Since dtype is a pointer, specify uintp. arr1. \ __array_interface \ __ ['data'] [0]'get the first pointer. Gets the number of bytes when one line is moved with 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
Use ctypes.Structure when passing a structure. Specify the type of the member variable in \ _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
Recommended Posts