这是为了什么
通常有必要为程序编写插件。 但是由于类的二进制不兼容,这些插件将必须使用与主程序相同的语言编写。 在C ++中,习惯上将虚拟函数表放在类中。 如果使用某些规则(不使用接口的多重继承)并使用抽象类,则可以实现运行在不同C ++编译器下编译的插件的功能。
在本文中,我将展示如何在C ++程序中使用通过Free Pascal Compiler编写的插件(仅是一个总体思路,而不是真正的插件)。
什么是VMT?
虚拟方法表(VMT)是协调表,而vtable是编程语言中用于支持动态匹配(或后期绑定)的机制。
C ++标准没有明确定义如何实现动态协调,但是编译器经常使用同一基本模型的某些变体。
通常,编译器为每个类创建一个单独的vtable。 创建对象后,指向该vtable的指针(称为虚拟表指针或vpointer(有时也称为vptr或vfptr))被添加为对象的隐藏成员(通常是第一个成员)。 编译器还在每个类的构造函数中生成“隐藏”代码,以使用相应vtable的地址初始化其对象。
(段落摘自维基百科。)
实施。
首先,我们需要围绕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 ();
其中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; }
如您所见,代码从pascal中的库中调用函数,并向它们传递指向pascal中插件实现的指针,该指针先前在创建类时已保存。 调用getNewPlugin实例化主程序中的插件类。
现在让我们讨论一下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.
该文件在pascal中实现了几乎相同的接口,并包装了插件函数以将函数导出到库。 注意,所有接口实现函数都包含指向类的指针作为第一个参数。 对于类方法,此参数作为第一个参数隐式传递,访问该类的方法和字段时需要使用此参数。 getNewPlugin函数用于获取C ++类中的指针。 Pascal代码作为库连接。
PS:我忘了提到pascal中的代码应该/应该最好包装在try / catch中,因为不应在此插件方法中引发异常。 插件应处理其异常并立即返回结果或以简单类型的形式作为单独的函数返回结果。
PS2:添加了有关免费功能的注释,并更改了其代码。 我不会在此处添加任何更改以保持对评论的遵守。 他还添加了有关使用getNewPlugin函数并删除第三方应用程序中的对象的注释。 尽管知道接口的人已经很清楚了。
→
样品来源