C / C ++ من Python (C API)

رئيسي

نواصل موضوع كيفية استدعاء C / C ++ من Python3 . الآن نستخدم واجهة برمجة التطبيقات C لإنشاء الوحدة النمطية ، في هذا المثال يمكننا معرفة كيف تبسط cffi والمكتبات الأخرى حياتنا. لأنه في رأيي هذه هي الطريقة الأكثر صعوبة.


C


مكتبة اختبار لشرح العمل مع المتغيرات العالمية والهياكل والوظائف مع الوسائط من أنواع مختلفة. في مقالاتي ، استخدم أشكالًا مختلفة من المكتبة نفسها ، اعتمادًا على الطريقة التي أستخدمها الآن. روابط إلى الطرق السابقة أدناه.
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); } 

الوحدة مطلوبة للإشارة إلى أنها ستتضمن: الوظائف والمتغيرات والهياكل العالمية. كل شيء من هذا القبيل يحتاج إلى وصف ، أصعب شيء لأنواع البيانات الخاصة به (بنية ...) يتم إنشاء هذا الملف تقريبًا بواسطة cffi .


للعمل ، يجب عليك توصيل ملفات الرأس:


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

أعلام تجميع:


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

الوظيفة التالية مسؤولة عن معالجة الوسائط:


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

الأولى هي حجة من النوع int ، لها تسمية الحرف i
ثاني تعويم / مزدوج - د
السلسلة الثالثة
يمكن العثور هنا على جميع التعيينات الممكنة لخطابات أنواع البيانات .


ننتقل الآن إلى وصف كيفية وصف الهيكل.
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 */ }; 

وهذا كله من أجل:


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

توافق ، ليس قليلا. علاوة على ذلك ، يمكن تعريف الطرق للبنية (كمثال ، test_st_print ).
في التعليمات البرمجية ، أحاول تقديم المزيد من التعليقات لوصف بشكل منفصل أقل.


الثعبان


مثال على العمل مع وحدة C من 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))) 

أصبحت الوحدة الأم.


إيجابيات وسلبيات C API


الايجابيات :


  • سهل الاستخدام في الثعبان

سلبيات :


  • من الصعب وصف أنواع البيانات الخاصة بك في C API
  • من الصعب على المبرمجين البحتين تنفيذ برنامج Python ، وليس بالنسبة لهم أيضًا ... (بالنسبة لي أبسط ما يكون عبر ctypes )
  • الوحدة (المكتبة) ستكون فقط لبيثون

يبدأ متوسط ​​وقت تنفيذ الاختبار في كل طريقة بـ 1000:


  • ctypes: - 0.0004987692832946777 seconds ---
  • CFFI: - 0.00038521790504455566 ثانية ---
  • pybind: - 0.0004547207355499268 ثانية ---
  • C API: - 0.0003561973571777344 ثانية ---

مراجع


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


All Articles