El 煤ltimo art铆culo de la serie sobre c贸mo invocar C / C ++ desde Python3 revis贸 todas las formas conocidas de hacerlo. Esta vez tengo que impulsar . Lo que sali贸 de esto se lee a continuaci贸n.
C
Tomo el mismo ejemplo de una biblioteca de prueba como base y lo convierto en una variaci贸n para un m茅todo espec铆fico. Una biblioteca de prueba para demostrar el trabajo con variables globales, estructuras y funciones con argumentos de varios tipos.
prueba.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; }
prueba.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
C贸mo compilar:
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
La fuente se compila en una biblioteca din谩mica.
python boost es similar en uso a pybind11 , tambi茅n debe describir las funciones que ver谩 python. Pero en mi opini贸n, el impulso es m谩s voluminoso y complejo. Por ejemplo:
def("func_ret_struct", &func_ret_struct, return_value_policy<reference_existing_object>());
La funci贸n func_ret_struct toma un puntero a una estructura como argumento y devuelve el mismo puntero. Para ello, debe especificar las reglas del objeto devuelto return_value_policy <reference_existing_object> () . reference_existing_objec dice que el objeto devuelto ya exist铆a. Si especifica manage_new_object, significar谩 que estamos devolviendo un nuevo objeto. En este caso, dicho script caer谩 en una falla de segmentaci贸n en el recolector de basura:
test_st = _test.test_st_t() ret = _test.func_ret_struct(test_st)
Porque el recolector de basura primero borrar谩 los datos que contiene test_st, y luego quiere borrar los datos que contiene el objeto ret. Que contiene los mismos datos que test_st conten铆a, pero ya se ha borrado.
驴Es interesante c贸mo en este caso describir una funci贸n de este tipo (no fue profunda)?
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)); } }
Tal funci贸n puede devolver un objeto existente as铆 como uno existente.
Tambi茅n tuve un problema con dicha funci贸n:
char * func_ret_str(char *val) { return val; }
Seg煤n tengo entendido, no puede obtener un puntero a un tipo de datos est谩ndar de python en boost. Solo es posible en struct , class y union . Si alguien sabe una manera de iluminarse.
Pit贸n
Para python, el m贸dulo se convierte en nativo.
main.py:
Pros y contras del impulso
Pros :
- sintaxis simple cuando se usa en Python
Contras :
- necesita editar fuentes C ++ o escribir un enlace para ellas
- impulsar solo no es f谩cil
El c贸digo, como siempre, trato de comentar claramente.
El tiempo promedio de ejecuci贸n de la prueba en cada m茅todo con 1000 comienza:
- tipos: - 0.0004987692832946777 segundos ---
- CFFI: - 0.00038521790504455566 segundos ---
- pybind: - 0.0004547207355499268 segundos ---
- API de C: - 0.0003561973571777344 segundos ---
- impulso: - 0.00037789344787597656 segundos ---
Referencias