C / C ++ von Python (CFFI, pybind11)

Haupt

Wir setzen das Thema fort, wie C / C ++ von Python3 aufgerufen wird . Jetzt verwenden wir die cffi , pybind11 Bibliotheken . Die Methode durch ctypes wurde in einem früheren Artikel diskutiert.


C.


Eine Testbibliothek zur Demonstration der Arbeit mit globalen Variablen, Strukturen und Funktionen mit Argumenten verschiedener Typen.
test.h


typedef struct test_st_s test_st_t; extern int a; extern double b; extern char c; 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); test_st_t *func_ret_struct(test_st_t *test_st); struct test_st_s { int val1; double val2; char val3; }; 

test.c


 #include <stdio.h> #include <stdlib.h> #include "test.h" 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; } char * func_ret_str(char *val) { printf("C get func_ret_str: %s\n", val); return 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; } 

Die Bibliothek ist genau die gleiche wie im Artikel ctypes .


CFFI


Dies ist eine Bibliothek für die ausschließliche Arbeit mit C. Aus der Beschreibung dieser Bibliothek:


Interagiere mit fast jedem C-Code aus Python

Einiges davon wurde fast gefunden.


Für das Experiment wurde Version 1.12.3 verwendet , darüber können Sie hier lesen.


Ein wenig über diese Bibliothek in zwei Worten: CFFI generiert seine Bindung über unserer Bibliothek und kompiliert sie zu einer Bibliothek, mit der wir arbeiten werden.


Installation


pip3 installiere cffi


Montage


Das Build-Skript, das die Bindung um unsere Bibliothek sammelt.


build.py


 import os import cffi if __name__ == "__main__": ffi = cffi.FFI() #    PATH = os.getcwd() # test.h     #      build.py with open(os.path.join(PATH, "src/c/test.h")) as f: ffi.cdef(f.read()) ffi.set_source("_test", #    cffi,   _ #  test.h,     _test '#include "../src/c/test.h"', #   libtest.so (  ) #  _test.cpython-36m-x86_64-linux-gnu.so ( CFFI) libraries=[os.path.join(PATH, "lib/test"), "./test"], library_dirs=[PATH, 'objs/'], ) #  _test   lib ffi.compile(tmpdir='./lib') 

Python


Ein Beispiel für die Arbeit mit C von Python über CFFI :


 from cffi import FFI import sys import time #    _test sys.path.append('.') sys.path.append('lib/') sys.path.append('../../lib/') #   import _test ### ## C ### print("CFFI\n") print("C\n") start_time = time.time() ## #    ## print('  :') print('ret func_ret_int: ', _test.lib.func_ret_int(101)) print('ret func_ret_double: ', _test.lib.func_ret_double(12.123456789)) #     cdata   ,     . print('ret func_ret_str: ', _test.ffi.string(_test.lib.func_ret_str('Hello!'.encode('utf-8'))).decode("utf-8")) print('ret func_many_args: ', _test.lib.func_many_args(15, 18.1617, 'X'.encode('utf-8'), 32000).decode("utf-8")) ## #    ## print('\n  :') print('ret a: ', _test.lib.a) #   . _test.lib.a = 22 print('new a: ', _test.lib.a) print('ret b: ', _test.lib.b) print('ret c: ', _test.lib.c.decode("utf-8")) ## #    ## print('\n  :') #      test_st = _test.ffi.new("test_st_t *") test_st.val1 = 5 test_st.val2 = 5.1234567 test_st.val3 = 'Z'.encode('utf-8') ret = _test.lib.func_ret_struct(test_st) #    C print('ret val1 = {}\nret val2 = {}\nret val3 = {}'.format(ret.val1, ret.val2, ret.val3.decode("utf-8"))) #   print("--- %s seconds ---" % (time.time() - start_time)) 

Um mit C ++ - Code arbeiten zu können, müssen Sie eine C- Bindung dafür schreiben. Der Artikel über die Methode durch ctypes beschreibt, wie dies getan wird. Link unten.


Vor- und Nachteile von CFFI


Vorteile :


  • einfache Syntax bei Verwendung in Python
  • Die Quellbibliothek muss nicht neu kompiliert werden

Nachteile :


  • Da dies keine bequeme Assembly ist, müssen Sie die Pfade zu allen Header-Dateien und Bibliotheken registrieren
  • Es wird eine weitere dynamische Bibliothek erstellt, die das Original verwendet
  • unterstützt die folgenden Richtlinien nicht:
     #ifdef __cplusplus extern "C" { #endif ... #ifdef __cplusplus } #endif 

     #ifndef _TEST_H_ #define _TEST_H_ ... #endif /* _TEST_H_ */ 

pybind11


Im Gegensatz dazu wurde pybind11 speziell für die Arbeit mit C ++ entwickelt . Für das Experiment wurde Version 2.3.0 verwendet, darüber können Sie hier lesen. Sie sammelt keine C-Quellen, deshalb habe ich sie in C ++ - Quellen übersetzt.


Installation


pip3 installiere pybind11


Montage


Wir müssen ein Build-Skript für unsere Bibliothek schreiben.
build.py


 import pybind11 from distutils.core import setup, Extension ext_modules = [ Extension( '_test', #    pybind11 ['src/c/test.cpp'], #     include_dirs=[pybind11.get_include()], #     pybind11 language='c++', #   extra_compile_args=['-std=c++11'], #  ++11 ), ] setup( name='_test', #    pybind11 version='1.0.0', author='djvu', author_email='djvu@inbox.ru', description='pybind11 extension', ext_modules=ext_modules, requires=['pybind11'], #    pybind11 package_dir = {'': 'lib'} ) 

Wir führen es aus:


 python3 setup.py build --build-lib=./lib 

C ++


In der Bibliotheksquelle müssen Sie Folgendes hinzufügen:


  • pybind11- Headerdatei
     #include <pybind11/pybind11.h> 
  • Makro, mit dem wir ein Python- Modul definieren können
     PYBIND11_MODULE(_test, m) 
  • Makrobeispiel für eine Testbibliothek:

 namespace py = pybind11; // _test    PYBIND11_MODULE(_test, m) { /* *   */ m.def("func_ret_int", &func_ret_int); m.def("func_ret_double", &func_ret_double); m.def("func_ret_str", &func_ret_str); m.def("func_many_args", &func_many_args); m.def("func_ret_struct", &func_ret_struct); /* *    */ m.attr("a") = a; m.attr("b") = b; m.attr("c") = c; /* *  */ py::class_<test_st_t>(m, "test_st_t") .def(py::init()) //  .    ,    Python //       C,  C++  (   C     ++   ) .def_readwrite("val1", &test_st_t::val1) //   .def_readwrite("val2", &test_st_t::val2) .def_readwrite("val3", &test_st_t::val3); }; 

Python


Ein Beispiel für die Arbeit mit C aus Python über pybind11 :


 import sys import time #    _test sys.path.append('lib/') #   import _test ### ## C ### print("pybind11\n") print("C\n") start_time = time.time() ## #    ## print('  :') print('ret func_ret_int: ', _test.func_ret_int(101)) print('ret func_ret_double: ', _test.func_ret_double(12.123456789)) #     cdata   . print('ret func_ret_str: ', _test.func_ret_str('Hello!'.encode('utf-8'))) print('ret func_many_args: ', _test.func_many_args(15, 18.1617, 'X'.encode('utf-8'), 32000)) ## #    ## print('\n  :') print('ret a: ', _test.a) #   . _test.a = 22 print('new a: ', _test.a) print('ret b: ', _test.b) print('ret c: ', _test.c) ## #    ## print('\n  :') #      _test_st = _test.test_st_t() #print(dir(_test_st)) _test_st.val1 = 5 _test_st.val2 = 5.1234567 _test_st.val3 = 'Z'.encode('utf-8') ret = _test.func_ret_struct(_test_st) #    C print('ret val1 = {}\nret val2 = {}\nret val3 = {}'.format(ret.val1, ret.val2, ret.val3)) #   print("--- %s seconds ---" % (time.time() - start_time)) 

Vor- und Nachteile von pybind11


Vorteile :


  • einfache Syntax bei Verwendung in Python

Nachteile :


  • Sie müssen C ++ - Quellen bearbeiten oder eine Bindung für sie schreiben
  • Es ist notwendig, die erforderliche Bibliothek aus der Quelle zu sammeln

Die durchschnittliche Testausführungszeit für jede Methode mit 1000 Starts:


  • ctypes: - 0,0004987692832946777 Sekunden ---
  • CFFI: - 0,00038521790504455566 Sekunden ---
  • pybind: - 0,0004547207355499268 Sekunden ---

+, - weil die Ergebnisse jedes Mal leicht unterschiedlich waren. Außerdem wurde Zeit für das Drucken aufgewendet, was ich zu faul war, um es auszuschalten (nehmen Sie diese Zeit als Konstante, da sie in allen Tests gleich ist). Trotzdem gibt es einen Zeitunterschied zwischen den Funktionsaufrufen und dem Abrufen der Ergebnisse.


Referenzen


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


All Articles