O artigo final da série sobre como chamar C / C ++ do Python3 passou por todas as formas conhecidas de fazer isso. Desta vez eu tenho que aumentar . O que veio dessa leitura abaixo.
C
Tomo o mesmo exemplo de uma biblioteca de teste como base e faço uma variação para um método específico. Uma biblioteca de teste para demonstrar o trabalho com variáveis, estruturas e funções globais com argumentos de vários tipos.
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
Como 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
A fonte é compilada em uma biblioteca dinâmica.
Como o python boost é semelhante ao pybind11 , você também precisa descrever as funções que o python verá. Mas, na minha opinião, o aumento é mais volumoso e complexo. Por exemplo:
def("func_ret_struct", &func_ret_struct, return_value_policy<reference_existing_object>());
A função func_ret_struct leva um ponteiro para uma estrutura como argumento e retorna o mesmo ponteiro. Para isso, é necessário especificar as regras do objeto retornado return_value_policy <reference_existing_object> () . reference_existing_objec diz que o objeto retornado já existia. Se você especificar manage_new_object, isso significa que estamos retornando um novo objeto. Nesse caso, esse script cairá em uma falha de segmentação no coletor de lixo:
test_st = _test.test_st_t() ret = _test.func_ret_struct(test_st)
Como o coletor de lixo limpa primeiro os dados que test_st contém e, em seguida, deseja limpar os dados que o objeto ret contém. Que contém os mesmos dados que test_st continha, mas já foram limpos.
É interessante como, neste caso, descrever essa função (não foi 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)); } }
Essa função pode retornar um objeto existente e um objeto existente.
Eu também tive um problema com essa função:
char * func_ret_str(char *val) { return val; }
Pelo que entendi, você não pode obter um ponteiro para um tipo de dados padrão do python no impulso. Só é possível em struct , classe e união . Se alguém souber uma maneira de esclarecer.
Python
Para python, o módulo se torna nativo.
main.py:
Prós e contras do impulso
Prós :
- sintaxe simples quando usada em Python
Contras :
- você precisa editar fontes C ++ ou escrever uma ligação para elas
- aumentar sozinho não é fácil
O código, como sempre, tento comentar claramente.
O tempo médio de execução do teste em cada método com 1000 inicia:
- ctypes: - 0.0004987692832946777 segundos ---
- CFFI: - 0.00038521790504455566 segundos ---
- pybind: - 0.0004547207355499268 segundos ---
- API C: - 0,0003561973571777344 segundos ---
- impulso: - 0.00037789344787597656 segundos ---
Referências