Untuk apa ini?
Sering kali perlu menulis plugin untuk program. Tetapi karena ketidakcocokan biner kelas, plugin ini harus ditulis dalam bahasa yang sama dengan program utama. Di C ++, sudah biasa menempatkan tabel fungsi virtual terlebih dahulu di kelas. Jika Anda menggunakan aturan tertentu (jangan menggunakan multiple inheritance of interfaces) dan menggunakan kelas abstrak, Anda dapat mencapai kemampuan untuk menjalankan plugin yang dikompilasi di bawah berbagai kompiler C ++.
Pada artikel ini saya akan menunjukkan cara menggunakan plugin yang ditulis menggunakan Free Pascal Compiler dalam program C ++ (hanya ide umum, bukan plugin nyata).
Apa itu VMT?
Tabel metode virtual (VMT) adalah tabel koordinat atau vtable adalah mekanisme yang digunakan dalam bahasa pemrograman untuk mendukung pencocokan dinamis (atau mengikat lebih lambat).
Standar C ++ tidak secara jelas mendefinisikan bagaimana koordinasi dinamis harus dilaksanakan, tetapi penyusun sering menggunakan beberapa variasi dari model dasar yang sama.
Biasanya, kompiler membuat vtable terpisah untuk setiap kelas. Setelah membuat objek, pointer ke vtable ini, disebut virtual table pointer atau vpointer (juga kadang-kadang disebut vptr atau vfptr), ditambahkan sebagai anggota objek yang tersembunyi (dan seringkali sebagai anggota pertama). Compiler juga menghasilkan kode "tersembunyi" dalam konstruktor masing-masing kelas untuk menginisialisasi objek vpointer'ov dengan alamat vtable yang sesuai.
(Paragraf diambil dari Wikipedia.)
Implementasi.
Pertama kita perlu membuat wrapper di sekitar kode dalam pascal.
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 ();
Di mana IPlugin adalah antarmuka plugin. Dan thisPascal adalah pointer ke versi biner dari kelas implementasi antarmuka dalam pascal.
Dan kode pembungkus itu sendiri:
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; }
Seperti yang Anda lihat, kode tersebut memanggil fungsi-fungsi dari library dalam pascal dan memberikan mereka pointer ke implementasi plugin dalam pascal yang sebelumnya disimpan saat membuat kelas. getNewPlugin dipanggil untuk instantiate kelas plugin di program utama.
Sekarang mari kita bicara tentang implementasi plugin di Pascal.
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.
File ini mengimplementasikan antarmuka yang hampir sama dalam pascal dan membungkus fungsi plug-in untuk mengekspor fungsi ke perpustakaan. Catatan semua fungsi implementasi antarmuka berisi pointer ke kelas sebagai parameter pertama. Parameter ini diteruskan secara implisit untuk metode kelas sebagai parameter pertama dan diperlukan untuk mengakses metode dan bidang kelas. Fungsi getNewPlugin digunakan untuk mendapatkan pointer di kelas C ++. Kode pascal terhubung sebagai perpustakaan.
PS: Saya lupa menyebutkan bahwa kode dalam pascal sebaiknya / sebaiknya dibungkus dengan try / catch karena pengecualian tidak boleh dibuang dalam metode plugin ini. Plugin harus menangani pengecualiannya dan mengembalikan hasilnya segera atau sebagai fungsi terpisah dalam bentuk tipe sederhana.
PS2: Menambahkan komentar tentang fungsi bebas dan mengubah kodenya. Saya tidak akan menambahkan perubahan apa pun di sini untuk menjaga kepatuhan terhadap komentar. Dan dia menambahkan komentar tentang menggunakan fungsi getNewPlugin dan menghapus objek di aplikasi pihak ketiga. Meskipun orang yang tahu tentang antarmuka, ini sudah jelas.
→
Sumber sampel