المقالة الأخيرة في السلسلة حول كيفية استدعاء C / C ++ من Python3 مرت بكل الطرق المعروفة للقيام بذلك. هذه المرة حصلت على تعزيز . ما جاء من هذا قراءة أدناه.
C
أأخذ نفس المثال لمكتبة الاختبار كأساس وأجعله تنوعًا لطريقة محددة. مكتبة اختبار لشرح العمل مع المتغيرات والبنى والوظائف العالمية باستخدام الوسائط من أنواع مختلفة.
test.c:
#include "test.hpp" 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; } object func_ret_str(char *val) { printf("C get func_ret_str: %s\n", val); return object(string(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; }
test.h:
using namespace boost::python; using namespace std; #ifdef __cplusplus extern "C" { #endif typedef struct test_st_s test_st_t; typedef char * char_p; extern int a; extern double b; extern char c; int func_ret_int(int val); double func_ret_double(double val); object 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; }; #ifdef __cplusplus } #endif
كيفية التجميع:
g++ -g -fPIC -I/usr/include/python3.6 -I./src/c -o ./objs/test.o -c ./src/c/test.cpp g++ -fPIC -g -shared -o ./lib/_test.so ./objs/test.o -lboost_python3
المصدر يجمع في مكتبة ديناميكية.
يشبه استخدام python boost pybind11 ، وتحتاج أيضًا إلى وصف الوظائف التي سيشاهدها python. ولكن في رأيي دفعة أكثر ضخمة ومعقدة. على سبيل المثال:
def("func_ret_struct", &func_ret_struct, return_value_policy<reference_existing_object>());
تأخذ الدالة func_ret_struct مؤشرًا إلى بنية كوسيطة وتقوم بإرجاع نفس المؤشر مرة أخرى. لذلك ، تحتاج إلى تحديد قواعد الكائن المرتجع return_value_policy <reference_existing_object> () . يشير reference_existing_objec إلى أن الكائن المرتجع موجود بالفعل. إذا حددت management_new_object ، فهذا يعني أننا نعيد كائنًا جديدًا. في هذه الحالة ، سوف يقع مثل هذا البرنامج النصي في خطأ تجزئة على أداة تجميع مجمعي البيانات المهملة:
test_st = _test.test_st_t() ret = _test.func_ret_struct(test_st)
لأن أداة تجميع مجمعي البيانات المهملة ستقوم أولاً بمسح البيانات التي تحتويها test_st ، ثم تريد مسح البيانات التي يحتوي عليها كائن ret. الذي يحتوي على نفس البيانات التي احتوت عليها test_st ، لكن تم مسحها بالفعل.
ومن المثير للاهتمام كيف في هذه الحالة لوصف مثل هذه الوظيفة (لم تذهب عميقة)؟
test_st_t * func_ret_struct(test_st_t *test_st) { if (test_st) { return test_st; } else { return (test_st_t *) malloc(sizeof(test_st_t)); } }
يمكن لمثل هذه الوظيفة إرجاع كائن موجود بالإضافة إلى كائن موجود.
واجهت أيضًا مشكلة في مثل هذه الوظيفة:
char * func_ret_str(char *val) { return val; }
كما أفهمها ، لا يمكنك الحصول على مؤشر إلى نوع بيانات قياسي من بيثون في دفعة. إنه ممكن فقط على الهيكل والطبقة والاتحاد . إذا كان أي شخص يعرف وسيلة لتنوير.
الثعبان
بالنسبة للثعبان ، تصبح الوحدة أصلية.
main.py:
إيجابيات وسلبيات دفعة
الايجابيات :
- بناء جملة بسيط عند استخدامه في بيثون
سلبيات :
- تحتاج إلى تحرير مصادر C ++ ، أو كتابة ارتباط لهم
- دفعة وحدها ليست سهلة
الرمز ، كالعادة ، أحاول التعليق بوضوح.
يبدأ متوسط وقت تنفيذ الاختبار في كل طريقة بـ 1000:
- ctypes: - 0.0004987692832946777 seconds ---
- CFFI: - 0.00038521790504455566 ثانية ---
- pybind: - 0.0004547207355499268 ثانية ---
- C API: - 0.0003561973571777344 ثانية ---
- دفعة: - 0.00037789344787597656 ثانية ---
مراجع