ماذا لو كان المحول البرمجي لا يدعم واجهات الإزاحة صفر VMT

ما هو؟


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

في هذه المقالة ، سأوضح كيفية استخدام المكوّن الإضافي المكتوب باستخدام Free Pascal Compiler في برنامج C ++ (فقط فكرة عامة ، وليس مكوّنًا إضافيًا حقيقيًا).

ما هو VMT؟


جدول الأسلوب الظاهري (VMT) هو جدول تنسيق أو vtable هو آلية تستخدم في لغات البرمجة لدعم المطابقة الديناميكية (أو الربط المتأخر).

لا تحدد معايير C ++ بوضوح كيفية تنفيذ التنسيق الديناميكي ، لكن المترجمين يستخدمون غالبًا بعض الاختلافات في نفس النموذج الأساسي.

عادة ، يقوم المترجم بإنشاء vtable منفصل لكل فئة. بعد إنشاء كائن ، تتم إضافة مؤشر إلى هذا vtable ، يسمى مؤشر جدول ظاهري أو مؤشر vpointer (يسمى أحيانًا vptr أو vfptr) كعضو مخفي في الكائن (وغالبًا كعضو أول). يقوم المترجم أيضًا بإنشاء كود "مخفي" في مُنشئ كل فئة لتهيئة كائنات vpointer'ov بعناوين vtable المقابلة.
(الفقرات مأخوذة من ويكيبيديا.)

التنفيذ.


نحتاج أولاً إلى إنشاء غلاف حول الكود في باسكال.

plugin.hpp
#pragma once #include "ApiEntry.hpp" class IPlugin { public: virtual void APIENTRY free () = 0; virtual void APIENTRY print () = 0; }; class Plugin : public IPlugin { public: virtual void APIENTRY free (); virtual void APIENTRY print (); Plugin (); virtual ~Plugin (); private: void* thisPascal; }; extern "C" IPlugin* APIENTRY getNewPlugin (); 

حيث IPlugin هو واجهة البرنامج المساعد. و thisPascal هو مؤشر إلى إصدار ثنائي من فئة تطبيق الواجهة في pascal.

ورمز الغلاف نفسه: plugin.cpp
 #include "plugin.hpp" #include "pascalunit.hpp" #include <iostream> void APIENTRY Plugin::free () { IPlugin_release (thisPascal); delete this; } void APIENTRY Plugin::print () { IPlugin_print (thisPascal); } Plugin::Plugin () { std::cout << "Plugin::Plugin" << std::endl; thisPascal = IPlugin_getNewPlugin (); } Plugin::~Plugin () { std::cout << "Plugin::~Plugin" << std::endl; } extern "C" IPlugin* APIENTRY getNewPlugin () { Plugin* plugin = new Plugin (); return plugin; } 

كما ترون ، يستدعي الرمز وظائف من المكتبة في باسكال ويمررها مؤشرًا إلى تنفيذ البرنامج المساعد في باسكال الذي تم حفظه مسبقًا عند إنشاء الفصل. يتم استدعاء getNewPlugin لإنشاء فئة البرنامج المساعد في البرنامج الرئيسي.

الآن دعونا نتحدث عن تنفيذ البرنامج المساعد في باسكال.

 library pascalunit; {$MODE OBJFPC} uses ctypes; type IPlugin = interface procedure _release (); cdecl; procedure print (); cdecl; end; TPlugin = class (TInterfacedObject, IPlugin) public procedure _release (); cdecl; procedure print (); cdecl; constructor Create (); destructor Free (); end; PPlugin = ^TPlugin; procedure TPlugin._release (); cdecl; begin Free; end; procedure TPlugin.print (); cdecl; begin writeln ('Hello World'); end; procedure _release (this: PPlugin); cdecl; begin this^._release (); end; procedure print (this: PPlugin); cdecl; begin this^.print (); end; constructor TPlugin.Create (); begin inherited; writeln ('TPlugin.Create'); end; destructor TPlugin.Free (); begin writeln ('TPlugin.Free'); end; function getNewPlugin (): PPlugin; cdecl; var plugin: PPlugin; begin New (plugin); plugin^ := TPlugin.Create (); result := plugin; end; exports getNewPlugin name 'IPlugin_getNewPlugin', print name 'IPlugin_print', _release name 'IPlugin_release'; begin end. 

يطبق هذا الملف تقريبًا نفس الواجهة في باسكال ويلتف حول وظائف المكون الإضافي لتصدير الوظائف إلى المكتبة. لاحظ أن جميع وظائف تنفيذ الواجهة تحتوي على مؤشر للفئة كمعلمة أولى. يتم تمرير هذه المعلمة بشكل ضمني لأساليب الفئة كمعلمة أولى وهي مطلوبة للوصول إلى طرق وحقول الفئة. يتم استخدام الدالة getNewPlugin للحصول على مؤشر في فئة C ++. رمز باسكال متصل كمكتبة.

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

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

مصادر العينة

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


All Articles