Приведение Class к Interface, от которого он унаслед.

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Сообщение trifon » 30.10.2007 13:23:35

И всё таки хочется конкретного примера, уже второй раз всё утыкается на - "В COM всё легко и просто делается при помощи магической процедуры(название процедуры с аборигенского на русский переводится как ритуальный танец с бубном вокруг костра)"

В данном коде можно заменить {$INTERFACES CORBA} на {$INTERFACES COM} и TMyClass = Class(IInterfaceA, IInterfaceB, IInterfaceC) на TMyClass = Class(TInterfacedObject,IInterfaceA, IInterfaceB, IInterfaceC) тогда получается, что он уже использует COM.

Я так и сделал, добавил к каждому интерфейсу свой GUID, заменил obj1 := obj3; и obj2 := TMyClass(obj1); на obj2 := obj1 as obj3; и получил:
Код: Выделить всё
Compiling interface6.pp
interface6.pp(67,17) Warning: Variable "obj1" does not seem to be initialized
interface6.pp(67,17) Error: Class or interface type expected, but got "TMyClass"
interface6.pp(79) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
make: *** [interface6] Ошибка 1
А в fpc мануале к примеру написано, что as является тем-же самым приведение типов, с предварительной проверкой на корректность, которая, как я понял при {$OBJECTCHECKS ON} выполняется автоматически при любом приведении объектов. Не понимаю чем as может помочь в данном случае?

И ещё вопрос какое отношение имеет COM к реализации в кроссплатформенном языке интерфейсов и делегирования? Делегирование вообще является одним из базовых элементов ООП.
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение Sergei I. Gorelkin » 30.10.2007 14:54:06

Я говорил о приведении класса к интерфейсу. Привести интерфейс к классу нельзя, и as в этом не поможет, равно как и использование Pointer.
Единственный способ - добавить в интерфейс лишний метод, который будет возвращать Self.

Что касается COM - это в случае FPC примерно такое же условное обозначение, как и CORBA. Просто эти интерфейсы имеют "предка" IUnknown, GUID и подсчет ссылок. Эти фичи совершенно кроссплатформенные.
Механизм QueryInterface() ("вмонтированный" в ключевое слово as) позволяет "приводить" интерфейсы друг к другу, а также позволяет объекту самому определять (во время выполнения), что и как он поддерживает.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение trifon » 30.10.2007 15:31:14

Sergei I. Gorelkin писал(а):Я говорил о приведении класса к интерфейсу.

Можно узнать что образуется физически в результате этой операции, сколько памяти это занимает, и время существования этого объекта(интерфейса), а также увидеть реальный пример использования этого.

Sergei I. Gorelkin писал(а):Механизм QueryInterface() ("вмонтированный" в ключевое слово as) позволяет "приводить" интерфейсы друг к другу, а также позволяет объекту самому определять (во время выполнения), что и как он поддерживает.


Как я понимаю интерфейсы, так-же как и классы, если один унаследован от другого, можно приводить друг к другу любым способом, ни as, ни QueryInterface() для этого не нужны.
Может быть as и QueryInterface() могут привести друг к другу интерфейсы не имеющие отношения одного к другому.
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение Sergei I. Gorelkin » 30.10.2007 16:47:11

trifon писал(а):Можно узнать что образуется физически в результате этой операции, сколько памяти это занимает, и время существования этого объекта(интерфейса), а также увидеть реальный пример использования этого.

Ничего не образуется. Берется указатель на готовый объект и прибавляется некоторое смещение. Действительным оно будет ровно столько же времени, сколько и сам объект.
Можно любой пример скомпилировать в ассемблер и посмотреть своими глазами.

trifon писал(а):Может быть as и QueryInterface() могут привести друг к другу интерфейсы не имеющие отношения одного к другому.

Совершенно верно. Кроме того, с их помощью можно запросить произвольный интерфейс у любого объекта. При этом объект не обязательно должен сам реализовывать этот интерфейс (то самое делегирование :)
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение trifon » 30.10.2007 18:50:05

Вместо того, что-бы в десятый раз говорить, что это легко и просто, может лучше продемонстрировать это, к примеру взять легко и просто поменять в своём-же некорректном коде несколько строчек так, что-бы там использовался obj1, после операции obj1 := obj3;.
А заодно и пример корректного приведения интерфейсов не имеющих отношения между собой.

А то весь этот пустой трёп не имеет никакого смысла.
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение Sergei I. Gorelkin » 30.10.2007 21:54:58

obj1 := obj3 - корректная операция. После нее интерфейс obj1 можно использовать. Некорректными являются операции с переменной obj2.

Вместо "obj1 := obj3" в случае с COM можно написать:
Код: Выделить всё
// Вариант1: будет выполнена runtime проверка, и кинуто исключение в случае
// если интерфейс не реализован
obj1 := obj3 as IInterfaceB;

// Вариант2: если не хочется исключений:
if Supports(obj3, IInterfaceB, obj1) then
begin
  // obj1 можно использовать
end;

// теперь можно взять сначала IInterfaceA и привести к IInterfaceB:
// (один из вариантов)
var
  ia: IInterfaceA;
begin
  ia := obj3;
  obj1 := ia as IInterfaceB;
end;
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Приведение Class к Interface, от которого он унаслед.

Сообщение AbakAngelSoft » 27.02.2010 15:57:07

Подниму старую тему - может кому нибудь пригодится.
Код: Выделить всё
const
  OffsetVTableOffset = 3;

function InterfaceToObject(AInterface: Pointer): TObject; inline;
begin
  if not Assigned(AInterface) then Exit(nil);
  Result := TObject(AInterface - Byte(((PPointerArray(AInterface^)^[0])+OffsetVTableOffset)^));
end;


Если этой функции передать интерфейс реализованный объектом fpc или delphi она вернет ссылку на объект реализующий его.
Работает только на платформах i386 и amd64 и только с объекта указанных языков. На остальных врядли будет работать.
Шаманизм чистой воды - используется тот факт что дла корректной обработки QueryInterface необходим Self и промежуточный код восстанавливает его из интерфейсной ссылки. На указанных архитектурах в 3-ем байте кода восстановления, находится константа указывающая размер смещения начала VTable интерфейса относительно начала объекта.

Никогда так не делайте :)
Аватара пользователя
AbakAngelSoft
постоялец
 
Сообщения: 273
Зарегистрирован: 06.08.2008 19:28:26
Откуда: Краснодар

Re: Приведение Class к Interface, от которого он унаслед.

Сообщение Odyssey » 27.02.2010 22:19:25

Если интерфейс пишется самостоятельно, а не пришёл из чужого кода, то по-моему надёжнее сделать в нём метод типа
Код: Выделить всё
function IMyIntf.GetObject: TObject;

а во всех реализующих его классах делать
Код: Выделить всё
function TMyIntfImpl.GetObject: TObject;
begin
  Result := Self;
end;

И это даже почти не шаманство :)
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: Приведение Class к Interface, от которого он унаслед.

Сообщение Павел Ишенин » 28.02.2010 17:59:46

В D2010 эта проблема решена в TObject
Павел Ишенин
постоялец
 
Сообщения: 475
Зарегистрирован: 24.03.2007 10:16:52

Re: Приведение Class к Interface, от которого он унаслед.

Сообщение AbakAngelSoft » 01.03.2010 10:25:58

Odyssey писал(а):Если интерфейс пишется самостоятельно

Естественно - если свой интерфейс то только так и надо делать!
И даже если класс свой - добавляем реализации своего интерфейса:
Код: Выделить всё
  ... = class(..., ..., IMyIntf)
...
end;

и в коде
Код: Выделить всё
procedure ...( I: IAlienInterface);
var
  MyI: IMyIntf;
begin
  MyI := I as IMyIntf;
  MyI.GetObject;
end;

Мой метод применим только если и интерфейс чужой и класс чужой.
Только вот мне кажется, что смешивать работу с объектом и интерфейсами им реализуемыми по меньшей мере неприлично.
Аватара пользователя
AbakAngelSoft
постоялец
 
Сообщения: 273
Зарегистрирован: 06.08.2008 19:28:26
Откуда: Краснодар

Re: Приведение Class к Interface, от которого он унаслед.

Сообщение rxt » 11.03.2013 16:01:09

Тема хоть и стара, но листая ответы, так и не удалось встретить ожидаемое чистое решение.

Sergei I. Gorelkin писал(а):Единственный способ - добавить в интерфейс лишний метод, который будет возвращать Self.

В точку и без шаманства.

Odyssey писал(а):
Код: Выделить всё
function IMyIntf.GetObject: TObject;

а во всех реализующих его классах делать
Код: Выделить всё
function TMyIntfImpl.GetObject: TObject;
begin
  Result := Self;
end;

И это даже почти не шаманство :)

На верном пути ...

Чистое решение:

Класс TComponent использует модель одновременной работы с объектами и итерфейсами, которые эти объекты реализуют.

Это позволяет компонентам реализовывать интерфейсы, и дать возможность интерфейсным свойствам использоваться в инспекторе объектов. А чтобы работать с опубликованными интерфейсными свойствами, необходим сам компонент для того, чтобы иметь возможность получить имя компонента, проверить его свойства и т.д..

Как раз TComponent использует чистое решение: он реализует интерфейс IInterfaceComponentReference с единственным методом GetComponent, который и возвращает компонент, реализующий интерфейс.

Определение взято из Delphi 7 "Classes.pas":

Код: Выделить всё
  IInterfaceComponentReference = interface
    ['{E28B1858-EC86-4559-8FCD-6B4F824151ED}']
    function GetComponent: TComponent;
  end;

В таком случае код можно написать так:

Код: Выделить всё
  TExampleObject = class;   

  IExampleInterfaceObjectReference = interface
    ['Ctrl + Shift + G']
    function GetObject: TExampleObject;
  end;
 
  TExampleObject = (TObject, IExampleInterfaceObjectReference, ...)
     ...
   function GetObject: TExampleObject; 
  end;
 
implementatoin // РЕАЛИЗАЦИЯ

   function TExampleObject.GetObject:TExampleObject;
   begin
      Result   :=   Self;
   end;      

Пример использования:

Код: Выделить всё
   var
      ExampleIntf  :IExampleObject;
      ExampleObj  :TExampleObject;
   begin
      
      ExampleObj   := (ExampleIntf as IExampleInterfaceObjectReference).GetObject;
      Caption      := ExampleObj.ClassName;   
   end;
      


Следует при этом учитывать, что обнуление ссылок на интерфейс может привести к разрушению объекта до его использования через объектную ссылку. У TComponent методы _AddRef и _Release возвращают -1 и не влияют на счётчик.

P.S.: А ведь некоторые ждали выхода Delphi 2010 :D.
rxt
новенький
 
Сообщения: 15
Зарегистрирован: 03.03.2013 13:54:02

Re:

Сообщение AlexVinS » 11.03.2013 16:13:25

Sergei I. Gorelkin писал(а):Присвоение 'интерфейс := класс' - это нормально. Компилятор знает о том, какие интерфейсы реализованы классом, поэтому он может сгенерировать правильный код.
Если же интерфейс не реализован классом, будет ошибка.

Собственно, а как еще можно вообще использовать CORBA-интерфейсы? В случае COM можно написать "MyObject as IMyInterface", или Supports(MyObject, IMyInterface, MyIntf). Но эти способы запрашивают интерфейс по GUID, который у CORBA отсутствует.


"MyObject as IMyInterface" вполне работает и с CORBA (но гуид или просто строка вместо него обязательны)
Аватара пользователя
AlexVinS
новенький
 
Сообщения: 95
Зарегистрирован: 27.01.2009 01:18:01

Пред.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 0

Рейтинг@Mail.ru