Программный интерфейс

Материал из CAMaaS preliminary wiki
Перейти к навигации Перейти к поиску

Под программным интерфейсом, если не оговорено иное, понимается набор число виртуальных функций, вызываемых над скрытым указателем на объект, либо согласно спецификации thiscall, либо (для обеспечения межъязыковой совместимости) stdcall с первым скрытым параметром - указателем на объект.

Пусть интерфейс определен как

struct IFace
{
   virtual void Method() = 0;
};

Такой интерфейс связан с объектом, обычно получаемым через компонент фабрику-класса

IClassFactory$ refFactory = get(); //Экземпляр порождающего класса, полученный откуда-то
IFace& inst = refFactory->CreateInstance(/*Аргументы*/);

либо через точку входа, имеющую связывание в стиле C

/*Импортируемая откуда-то функция, связывание C*/
extern "C" void* /*dllimport*/ GetObject(/*параметры*/);

struct IFace
{
   virtual void Method() = 0;
};

int main(int argc, char** argv)
{
   IFace* object_ptr = (struct IFace*) GetObject(/*аргументы*/);
   object_ptr->Method();
   return 0;
}

либо предоставляется как параметр какой-либо функции

void caller(IFace* object_ptr)
{
   object_ptr->Method();
}

Межъязыковая совместимость интерфейсов

Язык C++

Определение интерфейса
struct ICalculator
{
   virtual int __stdcall Add(int A, int B) = 0;
};

extern "C" void* __stdcall GetObject();
Реализация сервера
struct Implementation:ICalculator
{
   virtual int __stdcall Add(int A, int B);
};

int __stdcall Implementation::Add(int A, int B)
{
   return A + B;
}

extern "C" void* __stdcall GetObject()
{
   static Implementation instance;
   return &instance;
}
Реализация клиента
#include <iostream>

int main(int, char**)
{
	ICalculator* pObject = (ICalculator*) GetObject();
	std::cout << "Summ of 2 and 3 is " << pObject->Add(2, 3) << ".\n";
	return 0;
}

Язык C

Определение интерфейса

В языке нет понятия классов с методами, поэтому интерфейс определяется как структура с указателем на список указателей на функции-реализации методов интерфейса. Указатель на объект класса, над которым вызывается метод, передается в каждую функцию-метод как первый параметр. В архитектуре x86 и языке C++ этот параметр обычно передается через регистр ecx (см. thiscall), поэтому методы реализуются согласно спецификации stdcall, требующей передачу всех параметров через стек. Для x64 данное требование не применимо, так как в этом случае для функций и методов используется единая (для платформы) конвенция вызовов.

struct ICalculator;

struct ICalculator_functionlist
{
   int (__stdcall * Add)(struct ICalculator* pThis, int A, int B);
};

struct ICalculator
{
   struct ICalculator_functionlist* pFunc;
};

void* __stdcall GetObject();
Реализация сервера
int __stdcall Add(struct ICalculator* pThis, int A, int B)
{
	return A + B;
}

void* __stdcall GetObject()
{
	static struct ICalculator_functionlist vtbl = {Add};
	static struct ICalculator impl = {{&vtbl}};
	return &impl;
}
Реализация клиента
#include <stdio.h>

int main(int argc, char** argv)
{
	struct ICalculator* pInst = GetObject();
	printf("Summ of 2 and 3 is %d.\n", pInst->pFunc->Add(pInst, 2, 3));
	return 0;
}

Язык Free Pascal

Определение интерфейса

Free Pascal как и С++ - объектно-ориентированный. Но механизм вызова методов объектов отличается. Поэтому приходится взаимодействовать через указатель на список указателей на функции аналогично языку C.

type
   ICalcFuncHandle = ^ICalculatorFunctions;
   ICalculatorHandle = ^ICalculator;

   ICalculatorFunctions = record
     Add: function(pThis: ICalculatorHandle; a, b:integer): integer;stdcall;
   end;

   ICalculator = record
     pFunc:ICalcFuncHandle;
   end;
Реализация сервера
function Add(pThis: ICalculatorHandle; a,b:integer):integer;stdcall;
begin
 result:=a+b;
end;

function GetObject():ICalculatorHandle;stdcall;
var Calc:ICalculatorHandle;
begin
 Calc:=new(ICalculatorHandle);
 Calc^.pFunc^.Add := @Add;
 result:= Calc;
end;
Реализация клиента
const
{$ifdef win32}
  serverlib = 'server.dll';
{$else}
  {$ifdef darwin}
    serverlib = 'server';
    {$linklib server}
  {$else}
    serverlib = 'server.so';
  {$endif}
{$endif}

function GetObject():ICalculatorHandle; stdcall; external serverlib;

var pObject:ICalculatorHandle;
begin
  pObject:= GetObject(); 
  write('Sum of 2 and 3 is ');
  writeln(pObject^.pFunc^.Add(pObject, 2,3));
  readln()
end.


Язык Python

Реализация клиента
import ctypes
dllname = r'..\build\server.dll'

class ICalculator_functionlist(ctypes.Structure):
    _fields_ = [('Add', ctypes.c_void_p)]

class ICalculator(ctypes.Structure):
    _fields_ = [('pFunc', ctypes.POINTER(ICalculator_functionlist))]

dlllib = ctypes.windll.LoadLibrary(dllname)
dlllib.GetObject.restype = ctypes.c_void_p
pCalc = dlllib.GetObject()

calc = ctypes.cast (pCalc, ctypes.POINTER(ICalculator))
#ctypes.WINFUNCTYPE for stdcall, ctypes.CFUNCTYPE for cdecl
add_prototype = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_void_p,  ctypes.c_int, ctypes.c_int)

add = add_prototype(calc.contents.pFunc.contents.Add)
print('sum of 2 and 3 is: {}'.format(add(pCalc, 2,3)))