[Python] Comment appeler une fonction de c depuis python (édition ctypes)

Comment appeler une fonction de c depuis python (édition ctypes)

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.

  1. Créez une dll dans Visual Studio.
  2. Appel de python

1. Créer une DLL

Les DLL sont créées à l'aide de Visual Studio.

  1. Démarrez Visual Studio et sélectionnez "Créer un nouveau projet".
  2. Sélectionnez Dynamic-Link Library (DLL)
  3. Entrez le nom du projet (ctypes_sample1)
  4. Appuyez sur le bouton Créer
  5. Cliquez avec le bouton droit sur Fichiers source dans l'Explorateur de solutions, sélectionnez Ajouter-> Nouvel élément, sélectionnez Fichier C ++ (.cpp) et entrez le nom du fichier. (ctypes_sample1.cpp)
  6. Cliquez avec le bouton droit sur Fichiers d'en-tête dans l'Explorateur de solutions, sélectionnez Ajouter-> Nouvel élément, sélectionnez Fichier d'en-tête (.h) et saisissez le nom du fichier. (ctypes_sample1.h)
  7. Ecrire une fonction
  8. Créez une DLL avec Build-> Build Solution.

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

2. Appel de python

Placez la DLL créée directement sous .py.

Pour appeler une fonction, suivez la procédure ci-dessous.

  1. Chargez la dll avec ctypes.cdll.LoadLibrary
  2. Définissez les types d'arguments avec argtypes
  3. Définissez le type de retour avec restype

URL de référence: https://docs.python.org/3/library/ctypes.html

Pour les 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

Pour les pointeurs

Vous pouvez passer un tableau en tant qu'argument en utilisant un tableau de ctypes ou en utilisant numpy.

Tableau unidimensionnel

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

Tableau bidimensionnel

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

Lors du passage d'une structure

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

Recommended Posts

[Python] Comment appeler une fonction de c depuis python (édition ctypes)
Comment appeler une fonction
Comment ouvrir un navigateur Web à partir de python
Comment générer un objet Python à partir de JSON
Appel de scripts Python à partir de Python intégré en C ++ / C ++
[Go] Comment écrire ou appeler une fonction
Exécuter la fonction Python à partir de Powershell (comment passer des arguments)
Langage C pour voir et se souvenir de la partie 3 Appelez le langage C depuis Python (argument) c = a + b
Appelez Matlab depuis Python pour optimiser
Appeler C depuis Python avec DragonFFI
Appeler popcount depuis Ruby / Python / C #
Comment créer une fonction récursive
Python --Fonction d'appel dynamique à partir d'une chaîne
Comment envelopper C en Python
Comment accéder à wikipedia depuis python
Comment utiliser la fonction zip de python
Appeler C / C ++ depuis Python sur Mac
Appeler le langage C depuis Python (python.h)
Appeler des fonctions du langage C depuis Python pour échanger des tableaux multidimensionnels
Comment découper un bloc de plusieurs tableaux à partir d'un multiple en Python
Comment exécuter un programme Python à partir d'un script shell
Comment utiliser la méthode __call__ dans la classe Python
Comment lancer AWS Batch à partir de l'application cliente Python
Pour renvoyer char * dans une fonction de rappel à l'aide de ctypes en Python
Comment appeler Python ou Julia à partir de Ruby (implémentation expérimentale)
Comment créer un clone depuis Github
Je voulais créer une dll pour utiliser une fonction écrite en C à partir de Python dans ctypes, mais j'ai eu du mal
[Road to Intermediate Python] Appelez une instance de classe comme une fonction avec __call__
[Python] Comment rendre une classe itérable
[Python] Comment convertir une liste bidimensionnelle en liste unidimensionnelle
Comment mettre à jour Google Sheets à partir de Python
[Python 3.8 ~] Comment définir intelligemment des fonctions récursives avec des expressions lambda
Appelez Rust de Python pour accélérer! Tutoriel PyO3: Emballage d'une partie de fonction simple
[python] Comment utiliser __command__, explication des fonctions
Comment obtenir une chaîne à partir d'un argument de ligne de commande en python
[Python] Comment obtenir et modifier les lignes / colonnes / valeurs d'une table.
[Python] Comment inverser une chaîne de caractères
Comment obtenir stacktrace en python
Appelez Rust de Python pour accélérer! Tutoriel PyO3: Emballage d'une partie de fonction simple ➁
Comment accéder à RDS depuis Lambda (python)
Appelez dlm depuis python pour exécuter un modèle de régression à coefficient variable dans le temps
Comment créer un référentiel à partir d'un média
Passer la liste de Python vers C ++ par référence dans pybind11
Comment supprimer les doublons d'une liste Python tout en préservant l'ordre.
Appeler des commandes depuis Python (édition Windows)
Comment exécuter des scripts Maya Python
Langage C pour voir et se souvenir de la partie 2 Appeler le langage C à partir de la chaîne Python (argument)
Comment obtenir la valeur du magasin de paramètres dans lambda (en utilisant python)
Langage C pour voir et se souvenir de la partie 1 Appeler le langage C depuis Python (bonjour le monde)
[Introduction à Python] Comment écrire une chaîne de caractères avec la fonction format
Langage C pour voir et se souvenir de la partie 4 Appelez le langage C depuis Python (argument) double
Langage C pour voir et se souvenir de la partie 5 Appel du langage C à partir du tableau Python (argument)
Envoyer un message de Slack à un serveur Python
Exécuter des scripts Python à partir d'applications C # GUI
Comment utiliser la bibliothèque C en Python
Modifier Excel à partir de Python pour créer un tableau croisé dynamique
Comment lire un fichier CSV avec Python 2/3