четверг, 19 марта 2009 г.

Указатель на метод интерфейса

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

Но реальность оказалась не столь оптимистичной - на текущий момент Delphi такого функционала не поддерживает:
  • http://groups.google.com.ua/group/borland.public.delphi.objectpascal/browse_thread/thread/4c5dcb094b82522f?pli=1
  • http://qc.embarcadero.com/wc/qcmain.aspx?d=941 (а судя по датам и не будет поддерживать)
Самому раньше в таком функционале необходимости не было - проще передавать весь интерфейс, но если необходимо взаимодействие с чужим кодом, где могут требовать именно указатель на функцию, то прийдется изобретать не очень стройные решения:
  • метод в интерфейсе напрямую возвращающий адрес метода объекта
IAnIntf = interface
function GetPointerToProc: TAnObjectProc:
end
  • объект-обертку над интерфейсом
TWrapObj = class
private
AnIntf: ITheIntf;
protected
procedure AnProc(const Value: string);
end;

procedure TWrapObj.AnProc(const Value: string);
begin
AnIntf.AnProc(Value);
end;
  • возможно как-то можно будет выкрутиться через анонимные функции? нельзя, ибо
    TAnProc = procedure (S: string) of object;
    TAnProc2 = reference to procedure (S: string);
    это разные типы
Хотя и в реализации проблем более чем - вычислять статический адрес, на этапе компиляции, для такого указателя не получится, т.к. один и тот же интерфейс может реализовываться разными объектами.

> Still I think making (procedure of object) and (procedure of Interface) equivalent
> typeswould be the better solution.

Like I said, the semantics for the compiler would be quite different, so
it would make sense to make them different types (even if both
represented internally by a TMethod). Actually, if the interface
instance variable is nil, it is impossible to get the method address.
This is not a problem for objects, since there the static address is
taken.