C / C ++ de Python (ctypes)

principal

J'ai écrit sur la façon d'appeler Python à partir de C dans un article précédent, maintenant parlons comment faire le contraire et appeler C / C ++ à partir de Python3 . Une fois que j'aurai commencé à écrire à ce sujet, nous révélerons tout le sujet jusqu'à la fin. De plus, il n'y a rien de compliqué ici non plus.


C


Tout est simple ici, Python peut appeler des fonctions C sans aucun problème.


test.c:


#include "test.h" int a = 5; double b = 5.12345; char c = 'X'; int func_ret_int(int val) { printf("get func_ret_int: %d\n", val); return val; } double func_ret_double(double val) { printf("get func_ret_double: %f\n", val); return val; } char * func_ret_str(char *val) { printf("get func_ret_str: %s\n", val); return val; } char func_many_args(int val1, double val2, char val3, short val4) { printf("get func_many_args: int - %d, double - %f, char - %c, short - %d\n", val1, val2, val3, val4); return val3; } 

test.h:


 #ifndef _TEST_H_ #define _TEST_H_ #ifdef __cplusplus extern "C" { #endif #include <stdio.h> #include <string.h> #include <unistd.h> int func_ret_int(int val); double func_ret_double(double val); char *func_ret_str(char *val); char func_many_args(int val1, double val2, char val3, short val4) #ifdef __cplusplus } #endif #endif /* _TEST_H_ */ 

Comment compiler:


 gcc -fPIC -shared -o libtest.so test.c 

La source se compile dans une bibliothèque dynamique et est prête pour la bataille.
Nous passons à Python. L'exemple montre comment passer des arguments à une fonction, obtenir le résultat d'une fonction et comment obtenir et modifier les valeurs des variables globales.


main.py:


 #!/usr/bin/python3 #-*- coding: utf-8 -*- import ctypes #   test = ctypes.CDLL('./objs/libtest.so') ## #    ## # ,    int test.func_ret_int.restype = ctypes.c_int # ,     int test.func_ret_int.argtypes = [ctypes.c_int, ] # ,    double test.func_ret_double.restype = ctypes.c_double # ,     double test.func_ret_double.argtypes = [ctypes.c_double] # ,    char * test.func_ret_str.restype = ctypes.c_char_p # ,     char * test.func_ret_str.argtypes = [ctypes.POINTER(ctypes.c_char), ] # ,    char test.func_many_args.restype = ctypes.c_char # ,     int, double. char, short test.func_many_args.argtypes = [ctypes.c_int, ctypes.c_double, ctypes.c_char, ctypes.c_short] print('ret func_ret_int: ', test.func_ret_int(101)) print('ret func_ret_double: ', test.func_ret_double(12.123456789)) #      ,       . print('ret func_ret_str: ', test.func_ret_str('Hello!'.encode('utf-8')).decode("utf-8") ) print('ret func_many_args: ', test.func_many_args(15, 18.1617, 'X'.encode('utf-8'), 32000).decode("utf-8")) print() ## #    ## # ,    int a = ctypes.c_int.in_dll(test, "a") print('ret a: ', a.value) #   . a.value = 22 a = ctypes.c_int.in_dll(test, "a") print('ret a: ', a.value) # ,    double b = ctypes.c_double.in_dll(test, "b") print('ret b: ', b.value) # ,    char c = ctypes.c_char.in_dll(test, "c") print('ret c: ', c.value.decode("utf-8")) 

Tous les types de données possibles et leurs désignations se trouvent dans la documentation Python.


Travailler avec des structures


C - déclaration de structure dans test.h:


 typedef struct test_st_s test_st_t; struct test_st_s { int val1; double val2; char val3; }; 

Fonction pour travailler avec notre structure:


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

Python:


 import sys import struct #    Python   C class test_st_t(ctypes.Structure): _fields_ = [('val1', ctypes.c_int), ('val2', ctypes.c_double), ('val3', ctypes.c_char)] # ,    test_st_t * test.func_ret_struct.restype = ctypes.POINTER(test_st_t) # ,     void * test.func_ret_struct.argtypes = [ctypes.c_void_p] #   test_st = test_st_t(19, 3.5, 'Z'.encode('utf-8')) # Python None == Null C ret = test.func_ret_struct(None) print('ret func_ret_struct: ', ret) #   None,      ret = test.func_ret_struct(ctypes.byref(test_st)) #    C print('ret val1 = {}\nret val2 = {}\nret val3 = {}'.format(ret.contents.val1, ret.contents.val2, ret.contents.val3.decode("utf-8"))) 

C ++


C'est un peu plus compliqué ici, car les ctypes ne peuvent fonctionner qu'avec les fonctions C. Ce n'est pas un problème pour nous, juste C liera le code C ++.


Méthodes de classe C ++ et liaisons C:


 #include "test.hpp" /* *   */ std::string test::ret_str(std::string val) { std::cout << "get ret_str: " << val << std::endl; return val; } int test::ret_int(int val) { std::cout << "get ret_int: " << val << std::endl; return val; } double test::ret_double(double val) { std::cout << "get ret_double: " << val << std::endl; return val; } /* *  C    C++ */ //   test,     . test *test_new() { return new test(); } //   test. void test_del(test *test) { delete test; } /* *   . */ //    ret_str char *test_ret_str(test *test, char *val) { // char *  std::string std::string str = test->ret_str(std::string(val)); // std::string  char * char *ret = new char[str.length() + 1]; strcpy(ret, str.c_str()); return ret; } //    ret_int int test_ret_int(test *test, int val) { return test->ret_int(val); } //    ret_double double test_ret_double(test *test, double val) { return test->ret_double(val); } /* *   . */ //    a int test_get_a(test *test) { return test->a; } //    b double test_get_b(test *test) { return test->b; } //    c char test_get_c(test *test) { return test->c; } 

Mais il y a une mise en garde, la liaison doit être déclarée comme externe C. Pour que le compilateur ++ ne surcharge pas les noms des fonctions de liaison. S'il le fait, nous ne pourrons pas travailler avec nos fonctions via des ctypes.
test.hpp:


 #include <iostream> #include <string.h> class test { public: int a = 5; double b = 5.12345; char c = 'X'; std::string ret_str(std::string val); int ret_int(int val); double ret_double(double val); }; #ifdef __cplusplus extern "C" { #endif test *test_new(); void test_del(test *test); char *test_ret_str(test *test, char *val); int test_ret_int(test *test, int val); double test_ret_double(test *test, double val); int test_get_a(test *test); double test_get_b(test *test); char test_get_c(test *test); #ifdef __cplusplus } #endif 

Comment compiler:
g ++ -fPIC -shared -o libtestpp.so test.cpp


Python est tout aussi simple.


 #   testpp = ctypes.CDLL('./objs/libtestpp.so') # ,     testpp.test_new.restype = ctypes.c_void_p #   test test = testpp.test_new() ## #    ## # ,    char * testpp.test_ret_str.restype = ctypes.c_char_p # ,     void *  char * testpp.test_ret_str.argtypes = [ctypes.c_void_p, ctypes.c_char_p] # ,    int testpp.test_ret_int.restype = ctypes.c_int # ,     void *  int testpp.test_ret_int.argtypes = [ctypes.c_void_p, ctypes.c_int] # ,    double testpp.test_ret_double.restype = ctypes.c_double # ,     void *  double testpp.test_ret_double.argtypes = [ctypes.c_void_p, ctypes.c_double] print('  :') #   1-       print('ret test_ret_str: ', testpp.test_ret_str(test, 'Hello!'.encode('utf-8')).decode("utf-8")) print('ret test_ret_int: ', testpp.test_ret_int(test, 123)) print('ret test_ret_double: ', testpp.test_ret_double(test, 9.87654321)) ## #    ## # ,    int testpp.test_get_a.restype = ctypes.c_int # ,     void * testpp.test_get_a.argtypes = [ctypes.c_void_p] # ,    double testpp.test_get_b.restype = ctypes.c_double # ,     void * testpp.test_get_b.argtypes = [ctypes.c_void_p] # ,    char testpp.test_get_c.restype = ctypes.c_char # ,     void * testpp.test_get_c.argtypes = [ctypes.c_void_p] print('\n  :') print('ret test_get_a: ', testpp.test_get_a(test)) print('ret test_get_b: ', testpp.test_get_b(test)) print('ret test_get_c: ', testpp.test_get_c(test).decode("utf-8")) # ,     void * testpp.test_del.argtypes = [ctypes.c_void_p] #   testpp.test_del(test) 

Avantages et inconvénients des types


Avantages :


  • Vous pouvez connecter n'importe quelle bibliothèque C déjà compilée

Inconvénients :


  • En Python, vous devez décrire ce que les fonctions C retournent et acceptent comme arguments.

Le code a essayé de commenter clairement, d'écrire moins ici)


J'espère que ce sera utile.


Gratitude


DollaR84 pour son aide.
Palich239 pour les erreurs trouvées.


Les références


Source: https://habr.com/ru/post/fr466499/


All Articles