[Python] How to call a c function from python (ctypes)

How to call a c function from python (ctypes)

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.

  1. Create a dll in Visual Studio.
  2. Call from python

1. Creating a DLL

DLLs are created using Visual Studio.

  1. Start Visual Studio and select "Create a new project".
  2. Select Dynamic-Link Library (DLL)
  3. Enter the Project name (ctypes_sample1)
  4. Press the Create button
  5. Right-click on Source Files in Solution Explorer, select Add-> New Item, select C ++ File (.cpp), and enter the file name. (ctypes_sample1.cpp)
  6. Right-click on Header Files in Solution Explorer, select Add-> New Item, select Header File (.h), and enter the file name. (ctypes_sample1.h)
  7. Write a function
  8. Create a DLL with Build-> Build Solution.

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;
}

2. Call from python

Place the created dll directly under .py.

To call a function, follow the procedure below.

  1. Load the dll with ctypes.cdll.LoadLibrary
  2. Set the argument types with argtypes
  3. Set the return type with restype

Reference URL: https://docs.python.org/3/library/ctypes.html

For variables

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

For pointers

You can pass an array as an argument by using array of ctypes or by using numpy.

One-dimensional array

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

Two-dimensional array

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

When passing a structure

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

[Python] How to call a c function from python (ctypes)
How to call a function
Call a Python function from p5.js.
How to open a web browser from python
How to generate a Python object from JSON
Call a Python script from Embedded Python in C ++ / C ++
[Go] How to write or call a function
Execute Python function from Powershell (how to pass arguments)
How to create a kubernetes pod from python code
C language to see and remember Part 3 Call C language from Python (argument) c = a + b
Call Matlab from Python to optimize
How to write a Python class
Call C from Python with DragonFFI
Call popcount from Ruby / Python / C #
How to make a recursive function
Python-dynamically call a function from a string
How to wrap C in Python
How to access wikipedia from python
How to use python zip function
Call C / C ++ from Python on Mac
Call c language from python (python.h)
Call C language functions from Python to exchange multidimensional arrays
How to slice a block multiple array from a multiple array in Python
How to run a Python program from within a shell script
How to use the __call__ method in a Python class
How to launch AWS Batch from a python client app
To return char * in a callback function using ctypes in Python
How to call Python or Julia from Ruby (experimental implementation)
How to create a clone from Github
I wanted to create a dll to use a function written in C from Python with ctypes, but I had a hard time
[Road to Python Intermediate] Call a class instance like a function with __call__
[Python] How to make a class iterable
[Python] How to convert a 2D list to a 1D list
How to update Google Sheets from Python
[Python 3.8 ~] How to define a recursive function smartly with a lambda expression
Call Rust from Python to speed it up! PyO3 Tutorial: Wrapping a Simple Function Part ➀
[python] How to use __command__, function explanation
How to get a string from a command line argument in python
[Python] How to get & change rows / columns / values from a table.
[Python] How to invert a character string
How to get a stacktrace in python
Simple code to call a python program from Javascript on EC2
Call Rust from Python to speed it up! PyO3 Tutorial: Wrapping a Simple Function Part ➁
How to access RDS from Lambda (python)
Call dlm from python to run a time-varying coefficient regression model
How to create a repository from media
Pass a list by reference from Python to C ++ with pybind11
How to remove duplicates from a Python list while preserving order.
Call a command from Python (Windows version)
How to run a Maya Python script
C language to see and remember Part 2 Call C language from Python (argument) string
How to get a value from a parameter store in lambda (using python)
C language to see and remember Part 1 Call C language from Python (hello world)
[Introduction to Python] How to write a character string with the format function
C language to see and remember Part 4 Call C language from Python (argument) double
C language to see and remember Part 5 Call C language from Python (argument) Array
Send a message from Slack to a Python server
Run a Python script from a C # GUI application
How to use the C library in Python
Edit Excel from Python to create a PivotTable
How to read a CSV file with Python 2/3