Der letzte Artikel in der Reihe zum Aufrufen von C / C ++ aus Python3 ging alle bekannten Möglichkeiten durch. Diesmal muss ich mich steigern . Was dabei herauskam, lesen Sie weiter unten.
C.
Ich nehme das gleiche Beispiel einer Testbibliothek als Grundlage und mache daraus eine Variation für eine bestimmte Methode. Eine Testbibliothek zur Demonstration der Arbeit mit globalen Variablen, Strukturen und Funktionen mit Argumenten verschiedener Typen.
test.c:
#include "test.hpp" int a = 5; double b = 5.12345; char c = 'X'; int func_ret_int(int val) { printf("C get func_ret_int: %d\n", val); return val; } double func_ret_double(double val) { printf("C get func_ret_double: %f\n", val); return val; } object func_ret_str(char *val) { printf("C get func_ret_str: %s\n", val); return object(string(val)); } char func_many_args(int val1, double val2, char val3, short val4) { printf("C get func_many_args: int - %d, double - %f, char - %c, short - %d\n", val1, val2, val3, val4); return val3; } test_st_t * func_ret_struct(test_st_t *test_st) { if (test_st) { printf("C get test_st: val1 - %d, val2 - %f, val3 - %c\n", test_st->val1, test_st->val2, test_st->val3); } return test_st; }
test.h:
using namespace boost::python; using namespace std; #ifdef __cplusplus extern "C" { #endif typedef struct test_st_s test_st_t; typedef char * char_p; extern int a; extern double b; extern char c; int func_ret_int(int val); double func_ret_double(double val); object func_ret_str(char *val); char func_many_args(int val1, double val2, char val3, short val4); test_st_t *func_ret_struct(test_st_t *test_st); struct test_st_s { int val1; double val2; char val3; }; #ifdef __cplusplus } #endif
So kompilieren Sie:
g++ -g -fPIC -I/usr/include/python3.6 -I./src/c -o ./objs/test.o -c ./src/c/test.cpp g++ -fPIC -g -shared -o ./lib/_test.so ./objs/test.o -lboost_python3
Die Quelle wird in eine dynamische Bibliothek kompiliert.
Python Boost wird ähnlich wie Pybind11 verwendet . Sie müssen auch die Funktionen beschreiben, die Python sehen wird. Aber meiner Meinung nach ist Boost sperriger und komplexer. Zum Beispiel:
def("func_ret_struct", &func_ret_struct, return_value_policy<reference_existing_object>());
Die Funktion func_ret_struct verwendet einen Zeiger auf eine Struktur als Argument und gibt denselben Zeiger zurück. Dazu müssen Sie die Regeln des zurückgegebenen Objekts return_value_policy <reference_existing_object> () angeben. reference_existing_objec gibt an, dass das zurückgegebene Objekt bereits vorhanden war. Wenn Sie manage_new_object angeben, bedeutet dies, dass wir ein neues Objekt zurückgeben. In diesem Fall fällt ein solches Skript in einen Segmentierungsfehler auf dem Garbage Collector:
test_st = _test.test_st_t() ret = _test.func_ret_struct(test_st)
Weil der Garbage Collector zuerst die Daten löscht, die test_st enthält, und dann die Daten löschen möchte, die das ret-Objekt enthält. Welches die gleichen Daten enthält, die test_st enthielt, aber bereits gelöscht wurde.
Es ist interessant, wie man in diesem Fall eine solche Funktion beschreibt (ging nicht tief)?:
test_st_t * func_ret_struct(test_st_t *test_st) { if (test_st) { return test_st; } else { return (test_st_t *) malloc(sizeof(test_st_t)); } }
Eine solche Funktion kann sowohl ein vorhandenes als auch ein vorhandenes Objekt zurückgeben.
Ich hatte auch ein Problem mit einer solchen Funktion:
char * func_ret_str(char *val) { return val; }
Soweit ich weiß, können Sie von Python in Boost keinen Zeiger auf einen Standarddatentyp erhalten. Es ist nur auf Struktur , Klasse und Vereinigung möglich . Wenn jemand einen Weg kennt, um aufzuklären.
Python
Für Python wird das Modul nativ.
main.py:
Vor- und Nachteile von Boost
Vorteile :
- einfache Syntax bei Verwendung in Python
Nachteile :
- Sie müssen C ++ - Quellen bearbeiten oder eine Bindung für sie schreiben
- Boost allein ist nicht einfach
Den Code versuche ich wie immer klar zu kommentieren.
Die durchschnittliche Testausführungszeit für jede Methode mit 1000 Starts:
- ctypes: - 0,0004987692832946777 Sekunden ---
- CFFI: - 0,00038521790504455566 Sekunden ---
- pybind: - 0,0004547207355499268 Sekunden ---
- C API: - 0,0003561973571777344 Sekunden ---
- Boost: - 0,00037789344787597656 Sekunden ---
Referenzen