[Python] So rufen Sie eine Funktion von c aus Python auf (ctypes edition)

So rufen Sie eine Funktion von c aus Python auf (ctypes edition)

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.

  1. Erstellen Sie eine DLL in Visual Studio.
  2. Rufen Sie von Python aus

1. Erstellen einer DLL

DLLs werden mit Visual Studio erstellt.

  1. Starten Sie Visual Studio und wählen Sie "Neues Projekt erstellen".
  2. Wählen Sie Dynamic-Link Library (DLL).
  3. Geben Sie den Projektnamen ein (ctypes_sample1)
  4. Klicken Sie auf die Schaltfläche Erstellen
  5. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Quelldateien, wählen Sie Hinzufügen-> Neues Element, wählen Sie C ++ - Datei (.cpp) und geben Sie den Dateinamen ein. (ctypes_sample1.cpp)
  6. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Kopfdateien, wählen Sie Hinzufügen-> Neues Element, wählen Sie Kopfdatei (.h) und geben Sie den Dateinamen ein. (ctypes_sample1.h)
  7. Schreiben Sie eine Funktion
  8. Erstellen Sie eine DLL mit Build-> Build Solution.

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

2. Rufen Sie von Python aus

Platzieren Sie die erstellte DLL direkt unter .py.

Gehen Sie wie folgt vor, um eine Funktion aufzurufen.

  1. Laden Sie die DLL mit ctypes.cdll.LoadLibrary
  2. Legen Sie die Argumenttypen mit argtypes fest
  3. Legen Sie den Rückgabetyp mit Restype fest

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

Für Variablen

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

Für Zeiger

Sie können ein Array als Argument übergeben, indem Sie ein Array von ctypes oder numpy verwenden.

Eindimensionales Array

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

Zweidimensionales Array

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

Beim Übergeben einer Struktur

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

[Python] So rufen Sie eine Funktion von c aus Python auf (ctypes edition)
So rufen Sie eine Funktion auf
So öffnen Sie einen Webbrowser über Python
So generieren Sie ein Python-Objekt aus JSON
Rufen Sie Python-Skripte aus Embedded Python in C ++ / C ++ auf
[Go] So schreiben oder rufen Sie eine Funktion auf
Führen Sie die Python-Funktion von Powershell aus (wie Sie Argumente übergeben).
C-Sprache zum Sehen und Erinnern Teil 3 Rufen Sie die C-Sprache aus Python auf (Argument) c = a + b
Rufen Sie Matlab von Python zur Optimierung auf
Rufen Sie C von Python mit DragonFFI auf
Rufen Sie popcount von Ruby / Python / C # auf
So erstellen Sie eine rekursive Funktion
Python - Dynamische Aufruffunktion von String
So verpacken Sie C in Python
So greifen Sie über Python auf Wikipedia zu
Verwendung der Zip-Funktion von Python
Rufen Sie C / C ++ von Python auf dem Mac auf
Rufen Sie die c-Sprache von Python aus auf (python.h)
Rufen Sie C-Sprachfunktionen von Python auf, um mehrdimensionale Arrays auszutauschen
So schneiden Sie ein Block-Multiple-Array aus einem Multiple-Array in Python
So führen Sie ein Python-Programm in einem Shell-Skript aus
Verwendung der Methode __call__ in der Python-Klasse
So starten Sie AWS Batch über die Python-Client-App
So geben Sie char * in einer Rückruffunktion mit ctypes in Python zurück
Wie man Python oder Julia von Ruby aus aufruft (experimentelle Implementierung)
So erstellen Sie einen Klon aus Github
Ich wollte eine DLL erstellen, um eine in C von Python in ctypes geschriebene Funktion zu verwenden, aber es fiel mir schwer
[Road to Intermediate Python] Rufen Sie eine Klasseninstanz wie eine Funktion mit __call__ auf
[Python] Wie man eine Klasse iterierbar macht
[Python] So konvertieren Sie eine zweidimensionale Liste in eine eindimensionale Liste
So aktualisieren Sie Google Sheets von Python
[Python 3.8 ~] Wie man rekursive Funktionen mit Lambda-Ausdrücken intelligent definiert
Rufen Sie Rust von Python an, um schneller zu werden! PyO3-Tutorial: Umschließen einer einfachen Funktion Teil ➀
[Python] Verwendung von __command__, Funktionserklärung
So erhalten Sie eine Zeichenfolge aus einem Befehlszeilenargument in Python
[Python] So erhalten und ändern Sie Zeilen / Spalten / Werte aus einer Tabelle.
[Python] So invertieren Sie eine Zeichenfolge
Wie bekomme ich Stacktrace in Python?
Rufen Sie Rust von Python an, um schneller zu werden! PyO3-Tutorial: Umschließen einer einfachen Funktion Teil ➁
Zugriff auf RDS von Lambda (Python)
Rufen Sie dlm von Python aus auf, um ein zeitvariables Koeffizientenregressionsmodell auszuführen
So erstellen Sie ein Repository aus Medien
Übergeben Sie die Liste von Python an C ++ als Referenz in pybind11
So entfernen Sie Duplikate aus einer Python-Liste unter Beibehaltung der Reihenfolge.
Aufrufbefehle von Python (Windows Edition)
So führen Sie Maya Python-Skripte aus
C-Sprache zum Anzeigen und Erinnern Teil 2 Rufen Sie die C-Sprache aus der Python-Zeichenfolge (Argument) auf
So erhalten Sie den Wert aus dem Parameterspeicher in Lambda (mit Python)
C-Sprache zum Sehen und Erinnern Teil 1 Rufen Sie die C-Sprache aus Python auf (Hallo Welt)
[Einführung in Python] So schreiben Sie eine Zeichenfolge mit der Formatierungsfunktion
C-Sprache zum Anzeigen und Erinnern Teil 4 Rufen Sie die C-Sprache von Python (Argument) double auf
C-Sprache zum Anzeigen und Erinnern Teil 5 Rufen Sie die C-Sprache aus dem Python-Array (Argument) auf
Senden Sie eine Nachricht von Slack an einen Python-Server
Führen Sie Python-Skripte in C # -GUI-Anwendungen aus
Verwendung der C-Bibliothek in Python
Bearbeiten Sie Excel in Python, um eine Pivot-Tabelle zu erstellen
Lesen einer CSV-Datei mit Python 2/3