C / C ++ von Python (ctypes)

Haupt

Ich habe in einem früheren Artikel darüber geschrieben, wie man Python von C aus aufruft . Lassen Sie uns nun das Gegenteil besprechen und C / C ++ von Python3 aus aufrufen . Sobald ich anfing darüber zu schreiben, werden wir das ganze Thema bis zum Ende enthüllen. Darüber hinaus gibt es auch hier nichts Kompliziertes.


C.


Hier ist alles einfach, Python kann problemlos C-Funktionen aufrufen.


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_ */ 

So kompilieren Sie:


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

Die Quelle wird zu einer dynamischen Bibliothek kompiliert und ist bereit für den Kampf.
Wir gehen zu Python über. Das Beispiel zeigt, wie Argumente an eine Funktion übergeben, das Ergebnis einer Funktion abgerufen und die Werte globaler Variablen abgerufen und geändert werden.


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")) 

Alle möglichen Datentypen und ihre Bezeichnungen finden Sie in der Python- Dokumentation .


Mit Strukturen arbeiten


C - Strukturdeklaration in test.h:


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

Funktion für die Arbeit mit unserer Struktur:


 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 ++


Hier ist es etwas komplizierter, weil ctypes kann nur mit C- Funktionen arbeiten. Dies ist für uns kein Problem, nur C bindet den C ++ - Code.


C ++ - Klassenmethoden und C-Bindungen:


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

Es gibt jedoch eine Einschränkung: Die Bindung muss als extern C deklariert werden . Damit der ++ - Compiler die Namen der Bindungsfunktionen nicht überlädt. Wenn er dies tut, können wir nicht über ctypes mit unseren Funktionen arbeiten.
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 

So kompilieren Sie:
g ++ -fPIC -shared -o libtestpp.so test.cpp


Python ist genauso einfach.


 #   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) 

Vor- und Nachteile von ctypes


Vorteile :


  • Sie können jede bereits kompilierte C- Bibliothek verbinden

Nachteile :


  • In Python müssen Sie beschreiben, welche C- Funktionen als Argumente zurückgeben und akzeptieren.

Der Code versuchte klar zu kommentieren, hier weniger zu schreiben)


Ich hoffe es wird nützlich sein.


Dankbarkeit


DollaR84 für seine Hilfe.
Palich239 für die gefundenen Fehler.


Referenzen


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


All Articles