Это может работать только в C++ и Delphi, реализуя их таблицы виртуальных методов таким же образом. Это так?
Первоначально предполагалось, что класс Delphi не совместим с VMT с классом C++. Я думал, что, поскольку все классы Delphi происходят от TObject
, который объявляет виртуальные методы. Эти виртуальные методы появляются в VMT, я предположил, что эти методы появятся сначала в VMT. Однако выясняется, что компилятор устанавливает, что встроенные виртуальные методы TObject
имеют отрицательные индексы в VMT. Это означает, что пользовательские виртуальные методы (определяемые в подклассах TObject
) начинаются с индекса 0.
Это означает, что классы Delphi и C++ в вашем коде в вопросе действительно имеют совместимые VMT. Я считаю, что этот дизайн был сделан для поддержки COM в более ранних версиях Delphi. Чтобы создать резервную копию своих претензий, я отсылаю вас к documentation говорит, с моим акцентом:
Компоновка в ВМТ показано в следующей таблице. На 32-битных платформах, при положительных смещениях, VMT состоит из списка 32-битных указателей методов (указателей на 64-разрядные методы на 64-битной платформе) - по одному на пользовательский виртуальный метод в классе типа - - в порядке декларации. Каждый слот содержит адрес соответствующей точки входа виртуального метода. Этот макет - , совместимый с C++ v-table и с COM. При отрицательных смещениях VMT содержит ряд полей, которые являются внутренними для реализации Delphi.Приложения должны использовать методы, определенные в TObject, для запроса этой информации, поскольку макет, вероятно, изменится в будущих реализациях языка Delphi.
Следует подчеркнуть, что ничто в стандарте C++ не требует использования виртуальных машин для виртуальных методов, тем более что VMT реализуются. В действительности, каждый основной компилятор Windows имеет VMT, реализованные таким образом, чтобы поддерживать COM.
Вместо того, чтобы полагаться на такие детали реализации, вы можете использовать интерфейсы Delphi. Однако, как вы знаете, это COM-интерфейсы, поэтому вы должны реализовать IUnknown
. Вы говорите, что хотите избежать машин COM, но вам нужно добавить только IUnknown
. На мой взгляд, это не особенно тяжело. Я понимаю, что вы считаете, что вам нужно регистрировать CLSID, внедрять фабрики классов и так далее. Вы этого не сделаете. Вам просто нужно было бы реализовать IUnknown
.
Во всяком случае, если вы действительно настроены на избегая IUnknown
, то вы не можете использовать интерфейсы Delphi и имеют два варианта, насколько я могу сказать:
- Реализовать VMT вручную в коде Delphi. VMT - это всего лишь массив указателей на функции. Это приведет вас к коду, который выглядит так, как COM делает в C. Совершенно возможно, но не совсем приятно.
- Используйте подход, изложенный в вашем вопросе, и полагайтесь на детали реализации, которые
TObject
использует отрицательные индексы VMT для своих встроенных виртуальных методов.
компилируется для варианта кода 2 выглядит следующим образом:
Delphi
{$APPTYPE CONSOLE}
type
ICallable = class
public
procedure CallMe cdecl; virtual; abstract;
procedure CallMeAgain cdecl; virtual; abstract;
end;
MyCallable = class(ICallable)
public
procedure CallMe; override;
procedure CallMeAgain; override;
end;
procedure MyCallable.CallMe;
begin
Writeln('CallMe');
end;
procedure MyCallable.CallMeAgain;
begin
Writeln('CallMeAgain');
end;
const
dllname = 'C:\Users\heff\Desktop\Win32Project1\Debug\Win32Project1.dll';
function Callback(Callable: ICallable): Boolean; cdecl; external dllname;
var
Callable: ICallable;
begin
Callable := MyCallable.Create;
Writeln(Callback(Callable));
Callable.Free;
Readln;
end.
C++
#include <Windows.h>
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
class ICallable
{
public:
virtual void CallMe() = 0;
virtual void CallMeAgain() = 0;
};
extern "C" __declspec(dllexport) bool Callback(ICallable* callable)
{
callable->CallMe();
callable->CallMeAgain();
return true;
}
Выходной
CallMe
CallMeAgain
TRUE
Интерфейсы Delphi следуют соглашениям COM. Поэтому вам нужно следовать соглашениям COM на C++, см. Http://rvelthuis.de/articles/articles-cppobjs.html и http://www.scritub.com/stiinta/tutorials/visual-c-en/Sharing- Code-and-Objects-Betwe18279520.php – Johan
Ох и интерфейсы в Delphi объявляются с использованием ключевого слова 'interface'. Если вы настаиваете на объявлении интерфейса, отличного от COM, вы используете 'abstract class'. Реализация интерфейса происходит из 'TInterfacedObject'. – Johan
@Johan Я пытаюсь явно избегать всего COM-оборудования здесь. А также в моем случае я не проектирую компонент C++ COM. Я хочу иметь возможность обратного вызова к объекту Delphi «совместного использования» того же «интерфейса» с C++. В сущности, я хочу знать, сопоставляет ли класс Delphi виртуальные абстрактные методы с соответствующим классом C++ с чистыми виртуальными функциями. – BigONotation