C / C ++ dari Python (C API)

utama

Kami melanjutkan topik tentang cara memanggil C / C ++ dari Python3 . Sekarang kita menggunakan API C untuk membuat modul, dalam contoh ini kita dapat mengetahui bagaimana cffi dan perpustakaan lain menyederhanakan hidup kita. Karena menurut saya ini adalah cara yang paling sulit.


C


Pustaka uji untuk mendemonstrasikan bekerja dengan variabel global, struktur, dan fungsi dengan argumen dari berbagai jenis. Dalam artikel saya, saya menggunakan variasi dari perpustakaan yang sama, tergantung pada metode yang sekarang saya gunakan. Tautan ke metode sebelumnya di bawah ini.
test.h


typedef struct test_st_s test_st_t; extern int a; extern double b; extern char c; static PyObject *func_hello(PyObject *self, PyObject *args); static PyObject *func_ret_int(PyObject *self, PyObject *args); static PyObject *func_ret_double(PyObject *self, PyObject *args); static PyObject *func_ret_str(PyObject *self, PyObject *args); static PyObject *func_many_args(PyObject *self, PyObject *args); static PyObject *func_ret_struct(PyObject *self, PyObject *args); struct test_st_s { PyObject_HEAD //    ,    int val1; double val2; char val3; }; 

test.c


 //    static PyMethodDef methods[] = { {"func_hello", func_hello, METH_NOARGS, "func_hello"}, //    {"func_ret_int", func_ret_int, METH_VARARGS, "func_ret_int"}, //    {"func_ret_double", func_ret_double, METH_VARARGS, "func_ret_double"}, {"func_ret_str", func_ret_str, METH_VARARGS, "func_ret_str"}, {"func_many_args", func_many_args, METH_VARARGS, "func_many_args"}, {"func_ret_struct", func_ret_struct, METH_VARARGS, "func_ret_struct"}, {NULL, NULL, 0, NULL} }; //   static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "_test", "Test module", -1, methods }; //   PyMODINIT_FUNC PyInit__test(void) { PyObject *mod = PyModule_Create(&module); //    PyModule_AddObject(mod, "a", PyLong_FromLong(a)); // int PyModule_AddObject(mod, "b", PyFloat_FromDouble(b)); // double PyModule_AddObject(mod, "c", Py_BuildValue("b", c)); // char //   //    if (PyType_Ready(&test_st_t_Type) < 0) return NULL; Py_INCREF(&test_st_t_Type); PyModule_AddObject(mod, "test_st_t", (PyObject *) &test_st_t_Type); return mod; } /** *  ,  . */ int a = 5; double b = 5.12345; char c = 'X'; // 88 static PyObject * func_hello(PyObject *self, PyObject *args) { //   args,   warning  . puts("Hello!"); Py_RETURN_NONE; } /** *       int   . */ static PyObject * func_ret_int(PyObject *self, PyObject *args) { int val; //  -  if (PyTuple_Size(args) != 1) { PyErr_SetString(self, "func_ret_int args error"); } PyArg_ParseTuple(args, "i", &val); /* *  . * //   PyObject *obj = PyTuple_GetItem(args, 0); //     int/long if (PyLong_Check(obj)) { PyErr_Print(); } //  (PyObject *)  int val = _PyLong_AsInt(obj); */ printf("C get func_ret_int: %d\n", val); return Py_BuildValue("i", val); } /** *       double   . */ static PyObject * func_ret_double(PyObject *self, PyObject *args) { double val; if (PyTuple_Size(args) != 1) { PyErr_SetString(self, "func_ret_double args error"); } PyArg_ParseTuple(args, "d", &val); printf("C get func_ret_double: %f\n", val); return Py_BuildValue("f", val); } /** *  string   . */ static PyObject * func_ret_str(PyObject *self, PyObject *args) { char *val; if (PyTuple_Size(args) != 1) { PyErr_SetString(self, "func_ret_str args error"); } PyArg_ParseTuple(args, "s", &val); /* *  . * PyObject *obj = PyTuple_GetItem(args, 0); PyObject* pResultRepr = PyObject_Repr(obj); val = PyBytes_AS_STRING(PyUnicode_AsEncodedString(pResultRepr, "utf-8", "ERROR")); */ printf("C get func_ret_str: %s\n", val); return Py_BuildValue("s", val); } /** *       int, double, char *. */ static PyObject * func_many_args(PyObject *self, PyObject *args) { int val1; double val2; char *val3; if (PyTuple_Size(args) != 3) { PyErr_SetString(self, "func_ret_str args error"); } PyArg_ParseTuple(args, "ids", &val1, &val2, &val3); printf("C get func_many_args: int - %d, double - %f, string - %s\n", val1, val2, val3); return Py_BuildValue("ifs", val1, val2, val3); } static PyObject * func_ret_struct(PyObject *self, PyObject *args) { test_st_t *st; //    Python if (!PyArg_ParseTuple(args, "O", &st)) // O -   Py_RETURN_NONE; printf("C get test_st: val1 - %d, val2 - %f, val3 - %d\n", st->val1++, st->val2++, st->val3++); return Py_BuildValue("O", st); } 

Modul ini diperlukan untuk menunjukkan bahwa itu akan mencakup: fungsi, variabel global dan struktur. Setiap hal seperti itu perlu dijelaskan, hal yang paling sulit untuk tipe datanya (struktur ...) Kira-kira file seperti itu dihasilkan oleh cffi .


Agar berfungsi, Anda harus menghubungkan file header:


 #include <Python.h> #include <structmember.h> //     

Bendera kompilasi:


 $(python3-config --includes --ldflags) -fPIC 

Fungsi berikut bertanggung jawab untuk memproses argumen:


 PyArg_ParseTuple(args, "ids", &val1, &val2, &val3); 

Yang pertama adalah argumen tipe int, ia memiliki penunjukan huruf i
2nd float / double - d
String ketiga - dtk
Semua kemungkinan penunjukan huruf dari tipe data dapat ditemukan di sini.


Sekarang kita beralih ke deskripsi bagaimana menggambarkan struktur.
struct.c:


 //   static void test_st_t_dealloc(test_st_t* self) { Py_TYPE(self)->tp_free((PyObject*)self); } //   static PyObject * test_st_t_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { test_st_t *self; self = (test_st_t *)type->tp_alloc(type, 0); if (self != NULL) { self->val1 = 0; self->val2 = 0.0; self->val3 = 0; } return (PyObject *)self; } //  ,     static int test_st_t_init(test_st_t *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"val1", "val2", "val3", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|idb", kwlist, &self->val1, &self->val2, &self->val3)) return -1; return 0; } //       static PyMemberDef test_st_t_members[] = { {"val1", T_INT, offsetof(test_st_t, val1), 0, "int"}, {"val2", T_DOUBLE, offsetof(test_st_t, val2), 0, "double"}, {"val3", T_CHAR, offsetof(test_st_t, val3), 0, "char"}, {NULL} }; //  ,    static PyObject* test_st_print(PyObject *self, PyObject *args) { test_st_t *st; //    Python if (!PyArg_ParseTuple(args, "O", &st)) // O -   Py_RETURN_NONE; printf("method: val1 - %d, val2 - %f, val3 - %d\n", st->val1++, st->val2++, st->val3++); Py_RETURN_NONE; } //   ,        ! //   ! static PyMethodDef test_st_t_methods[] = { {"print", test_st_print, METH_VARARGS, "doc string"}, {NULL} /* Sentinel */ }; //    .  , , ,   ..  .. PyTypeObject test_st_t_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_test.test_st_t", /* tp_name */ sizeof(test_st_t), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) test_st_t_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "test_st_t objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ test_st_t_methods, /* tp_methods */ test_st_t_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) test_st_t_init, /* tp_init */ 0, /* tp_alloc */ test_st_t_new, /* tp_new */ }; 

Dan itu saja untuk:


 struct test_st_s { PyObject_HEAD //    ,    int val1; double val2; char val3; }; 

Setuju, tidak sedikit. Selain itu, metode dapat didefinisikan untuk struktur (sebagai contoh, test_st_print ).
Dalam kode, saya mencoba membuat lebih banyak komentar untuk menjelaskan secara terpisah lebih sedikit.


Python


Contoh bekerja dengan modul C dari Python :


 import sys import time #    _test sys.path.append('.') sys.path.append('lib/') sys.path.append('../../lib/') import _test ### ## C ### print("C API\n") print("C\n") start_time = time.time() ## #    ## print('  :') print('ret func_hello: ', _test.func_hello()) 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!')) print('ret func_many_args: ', _test.func_many_args(15, 18.1617, "Many arguments!")) ## #    ## 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  :') #   st = _test.test_st_t(1, 2.3456789, 88) print('st.val1 = {}\nst.val2 = {}\nst.val3 = {}'.format(st.val1, st.val2, st.val3)) st = _test.func_ret_struct(st) print("ret func_ret_struct:") print('st.val1 = {}\nst.val2 = {}\nst.val3 = {}'.format(st.val1, st.val2, st.val3)) #   print  ,    C   #           st.print(st) #   print("--- {} seconds ---".format((time.time() - start_time))) 

Modul telah menjadi asli.


Pro dan Kontra dari API C


Pro :


  • mudah digunakan di python

Cons :


  • sulit untuk menggambarkan tipe data Anda dalam C API
  • sulit bagi programmer murni untuk mengimplementasikan Python , dan juga tidak untuk mereka ... (bagi saya yang paling sederhana adalah melalui ctypes )
  • module (library) hanya untuk Python

Waktu pelaksanaan pengujian rata-rata pada setiap metode dengan 1000 dimulai:


  • ctypes: - 0,0004987692832946777 detik ---
  • CFFI: - 0,00038521790504455566 detik ---
  • pybind: - 0,0004547207355499268 detik ---
  • API C: - 0,00035619735717773444 detik ---

Referensi


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


All Articles