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

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

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

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

Сообщение trifon » 29.10.2007 00:58:49

Есть код
Код: Выделить всё
{$Mode objfpc}
{$OBJECTCHECKS ON}
{$INTERFACES CORBA}

Program Simple_interface;

type
  IMyInterface = Interface
    function MyFunc : String;
  end;

type
  TMyClass = Class(IMyInterface)
    function MyFunc : String;
  end;

function TMyClass.MyFunc : String;
begin
end;

var
  obj1 : IMyInterface;
  obj2 : TMyClass;
  ptr  : Pointer;
begin
  obj1 := TMyClass.create();
  obj2 := TMyClass(obj1);
end.


при компиляции получаем
Код: Выделить всё
ppc386   -g -gv -dDEBUG -dGDB interface5.pp
Compiling Debug Version
Free Pascal Compiler version 2.2.0 [2007/10/04] for i386
Copyright (c) 1993-2007 by Florian Klaempfl
Target OS: Linux for i386
Compiling interface5.pp
interface5.pp(17,19) Warning: Function result does not seem to be set
interface5.pp(28,11) Warning: Class types "IMyInterface" and "TMyClass" are not related
interface5.pp(28,11) Error: class type expected, but got "IMyInterface"
interface5.pp(29,4) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
make: *** [interface5] Ошибка 1


если убрать - obj2 := TMyClass(obj1);
и добавить:
ptr := Pointer(obj1);
obj2 := TMyClass(ptr);
ошибок не будет

ещё если определить {$OBJECTCHECKS OFF}, тоже ошибок не будет.

Отсюда вопрос приведение класса к интерфейсу, от которого он унаследован некорректно?
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение Sergei I. Gorelkin » 29.10.2007 05:45:31

Суть вопроса в обратном - приведении интерфейса к классу (есть интерфейс, хотим получить класс). Это некорректно. Классы не наследуются от интерфейсов, они их реализуют (хотя записывается это так же, как и наследование). При этом интерфейс ничего не знает о том, кто его реализует, это может быть вообще не класс.

Через pointer - можно заставить скомпилиться, но работать это не будет.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение trifon » 29.10.2007 14:49:59

Собственно это я и имел в виду, ошибся.
А как же быть ясли я оперирую интерфейсом, а в какой-то момент мне понадобиться доступ к членам реализации, которые не объявлены в интерфейсе.
Например есть интерфейсы - list и iterator, реализация iterator должна иметь доступ к данным внутри реализации list, однако интерфейс знать о структуре данных внутри своей реализации не должен.
К тому-же {$OBJECTCHECKS ON} считает правильным приведение указателей к классу, можно любое дерьмо привести к классу, и попытаться его использовать, и это будет считаться правильным.
Конечно проблема решается {$OBJECTCHECKS OFF}, но - хочется, чтобы было красиво!
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение trifon » 29.10.2007 14:57:08

Sergei I. Gorelkin писал(а):Через pointer - можно заставить скомпилиться, но работать это не будет.


Как это не будет если pointer содержит этот класс, то всё прекрасно работает
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение Sergei I. Gorelkin » 29.10.2007 18:59:55

trifon писал(а):Например есть интерфейсы - list и iterator, реализация iterator должна иметь доступ к данным внутри реализации list, однако интерфейс знать о структуре данных внутри своей реализации не должен.

В таких случаях, как правило, в интерфейс list добавляется метод get_iterator (возвращает интерфейс iterator). В реализации этого метода ограничения интерфейса уже не действуют (это же метод класса, реализующего list), поэтому есть доступ ко всему что нужно. Создается объект TIterator, имеющий доступ к объекту TList, но возвращается интерфейс iterator, скрывающий эти подробности.

trifon писал(а):Как это не будет если pointer содержит этот класс, то всё прекрасно работает

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

Сообщение trifon » 29.10.2007 20:29:50

Я так и делал
Код: Выделить всё
function TCellList.getIterator : IIterator;
begin
    getIterator  := TCellIterator.create( TCellList(self.getRef) );
end;
self.getRef - возвращает указатель на базовый интерфейс - прототип, просто передать self нельзя, нужно для подсчета ссылок.

при {$OBJECTCHECKS ON} не могу привести этот self к TCellList, если {$OBJECTCHECKS OFF}, проверял, всё работает как надо.
Sergei I. Gorelkin писал(а):В приведенном выше примере метод пустой и не обращается к Self. Поэтому он будет работать вообще с каким угодно указателем.
В общем же случае указатель на интерфейс не равен указателю на экземпляр класса. При приведении класса к интерфейсу компилятор добавляет некоторое смещение.
Сказанное верно для COM интерфейсов, что касается CORBA, мне нужно самому подучить матчасть (я не смотрел, как они устроены внутри), но вряд ли различия велики.

А как-же работает вот это:
Код: Выделить всё
{$Mode objfpc}
{$OBJECTCHECKS OFF}
{$INTERFACES CORBA}

Program Simple_interface;

type
  IInterfaceA = Interface
    function mtd1 : String;
  end;

  IInterfaceB = Interface
    function mtd2 : String;
  end;

  IInterfaceC = Interface
    function mtd3 : String;
  end;

type
  TMyClass = Class(IInterfaceA, IInterfaceB, IInterfaceC)
    prop : String;
    function mtd1 : String;
    function mtd2 : String;
    function mtd3 : String;
  end;


function TMyClass.mtd1 : String;
begin
  mtd1 := 'mtd1'
end;
function TMyClass.mtd2 : String;
begin
  mtd2 := 'mtd2'
end;
function TMyClass.mtd3 : String;
begin
  mtd3 := 'mtd3'
end;

var
  obj1 : IInterfaceB;
  obj2 : TMyClass;
begin
  obj1 := TMyClass.create();

  obj2 := TMyClass(obj1);
  writeln('obj1 = obj2 ? ', Pointer(obj1) = Pointer(obj2));
  obj2.prop := 'IInterfaceB';
  writeln(obj2.prop, ' ', obj2.mtd1, ' ', obj2.mtd2, ' ', obj2.mtd2)

end.

Что касается CORBA, то я о ней тоже ничего не знаю. В мануале fpc написано, что {$INTERFACES CORBA} убирает всякий мусор предназначенный для COM, и может использоваться не только для CORBA. Как я понял в fpc для CORBA нет никакой поддержки.
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение Sergei I. Gorelkin » 29.10.2007 21:06:29

trifon писал(а):self.getRef - возвращает указатель на базовый интерфейс - прототип, просто передать self нельзя, нужно для подсчета ссылок.

Почему нельзя? В конструктор передать Self, внутри конструктора вызывать getRef.
А вообще, пользоваться интерфейсами без автоматического подсчета ссылок для того, чтобы поверх них реализовать собственный подсчет ссылок - со стороны выглядит довольно странно...

trifon писал(а):А как же работает вот это:


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

Код: Выделить всё
var
  obj1: IInterfaceB;
  obj2: TMyClass;
  obj3: TMyClass;
begin
  obj3 := TMyClass.Create;
  obj1 := obj3;
  obj2 := TMyClass(obj1);
 
  writeln('obj1 = obj2 ? ', Pointer(obj1) = Pointer(obj2));
  writeln('obj1 = obj3 ? ', Pointer(obj1) = Pointer(obj3)); 
  obj2.prop := 'IInterfaceB';
  obj3.prop := 'Property_3';
  writeln(obj2.prop, ' ', obj3.prop, ' ', obj2.mtd1, ' ', obj2.mtd2, ' ', obj2.mtd2);
end.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение trifon » 29.10.2007 22:00:36

Не знаю что происходит при obj1 := obj3;, ибо это означает присвоение класса TMyClass интерфейсу IInterfaceB, однако если заменить эту строку на pointer(obj1) := pointer(obj3) или на TMyClass(obj1) := obj3, всё работает как мною и задумано, и вообще эта строка выглядит как-то странно, всё равно, что byte := word, неплохо было-бы проверить каким нибудь трасировщиком памяти, возможно при этой операции образуется клон объекта.
Последний раз редактировалось trifon 29.10.2007 23:53:05, всего редактировалось 2 раз(а).
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение trifon » 29.10.2007 22:07:33

Может быть за подобный шаманизм многие и не любят Дельфи?
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение trifon » 29.10.2007 22:29:52

Sergei I. Gorelkin писал(а):А вообще, пользоваться интерфейсами без автоматического подсчета ссылок для того, чтобы поверх них реализовать собственный подсчет ссылок - со стороны выглядит довольно странно...

А в fpc мануале к примеру написано, что для CORBA автоматический подсчёт ссылок необязателен:
CORBA interfaces are not necessarily reference counted.
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение Sergei I. Gorelkin » 30.10.2007 00:17:26

Никакого шаманизма. Просто не нужно пытаться обманывать компилятор и самого себя...

Две строки в моем варианте (obj3 := TMyClass.Create; obj1 := obj3) в точности соответствуют одной строке из твоего (obj1 := TMyClass.Create), только у меня дополнительно получаем указатель на сам объект (obj3). Этот указатель не равен указателю на интерфейс, в данном случае они различаются на 264.
После этого obj2 получаем равным obj1, а он должен получиться равным obj3.

Если написать pointer(obj1) := pointer(obj3), то попробуй потом у этого интерфейса что-нибудь вызвать...
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение trifon » 30.10.2007 01:28:09

Если написать pointer(obj1) := pointer(obj3), то попробуй потом у этого интерфейса что-нибудь вызвать...

Я же писал - в коде заменяем obj1 := obj3 на pointer(obj1) := pointer(obj3),
после этого там выполняется
Код: Выделить всё
obj2 := TMyClass(obj1);
...
obj2.prop := 'IInterfaceB';
obj3.prop := 'Property_3';
writeln(obj2.prop, ' ', obj3.prop, ' ', obj2.mtd1, ' ', obj2.mtd2, ' ', obj2.mtd2);
вот результат
Код: Выделить всё
obj1 = obj2 ? TRUE
obj1 = obj3 ? TRUE
Property_3 Property_3 mtd1 mtd2 mtd2
всё выполняется , и ясно видно, что obj2 = obj3

И тогда второй вопрос, если пишем так
obj1 := obj3;
obj2 := TMyClass(obj1);
что-же тогда находится в obj2, там явно экземпляр TMyClass, при попытке вызвать obj2.destroy; - получаем
Runtime error 210 at $08049FBE
$08049FBE
$0805FB71

при попытке вызвать obj3.destroy; - получаем
Код: Выделить всё
*** glibc detected *** ./interface6: double free or corruption (!prev): 0x08067008 ***
======= Backtrace: =========
/lib/libc.so.6[0xb7e6b9f2]
/lib/libc.so.6(cfree+0x87)[0xb7e6d697]
./interface6[0x805f3ba]
./interface6[0x8059472]
./interface6[0x8053b74]
./interface6[0x805fb71]
======= Memory map: ========
08048000-08060000 r-xp 00000000 03:04 197195     /home/wow/devel/pascal/object/interface6
08060000-08061000 r-xp 00017000 03:04 197195     /home/wow/devel/pascal/object/interface6
08061000-08065000 rwxp 00018000 03:04 197195     /home/wow/devel/pascal/object/interface6
08065000-08088000 rwxp 08065000 00:00 0          [heap]
b7cf5000-b7cff000 r-xp 00000000 03:04 3351066    /usr/lib/gcc/i686-pc-linux-gnu/4.1.2/libgcc_s.so.1
b7cff000-b7d00000 rwxp 00009000 03:04 3351066    /usr/lib/gcc/i686-pc-linux-gnu/4.1.2/libgcc_s.so.1
b7d00000-b7d21000 rwxp b7d00000 00:00 0
b7d21000-b7e00000 ---p b7d21000 00:00 0
b7e06000-b7e07000 rwxp b7e06000 00:00 0
b7e07000-b7f2f000 r-xp 00000000 03:04 4075796    /lib/libc-2.6.1.so
b7f2f000-b7f31000 r-xp 00128000 03:04 4075796    /lib/libc-2.6.1.so
b7f31000-b7f32000 rwxp 0012a000 03:04 4075796    /lib/libc-2.6.1.so
b7f32000-b7f36000 rwxp b7f32000 00:00 0
b7f57000-b7f58000 r-xp b7f57000 00:00 0          [vdso]
b7f58000-b7f72000 r-xp 00000000 03:04 4075968    /lib/ld-2.6.1.so
b7f72000-b7f73000 r-xp 00019000 03:04 4075968    /lib/ld-2.6.1.so
b7f73000-b7f74000 rwxp 0001a000 03:04 4075968    /lib/ld-2.6.1.so
bfa03000-bfa18000 rwxp bfa03000 00:00 0          [stack]
Аварийный останов

причём я не пытаюсь освободить оба, не получается освободить ничего. Вопрос, как осврбодить obj3, и как долго всё это должно висеть в памяти.
Рекомендую попробовать в своём коде освободить obj3, весьма забавно.
Разьве это не шаманство?
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение Sergei I. Gorelkin » 30.10.2007 01:53:29

Да чего уж забавного... obj2 - левый указатель, в строке "obj2.prop := 'IInterfaceB'" мы затираем память где-то в районе переходника на интерфейс (странно то, что все не рушится уже на этой строке), и то, что после этого уже и obj3 не освобождается - совсем не удивительно.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Сообщение trifon » 30.10.2007 02:03:10

А мне странно что всё это не пресекается, ещё на этапе компиляции,
присвоение интерфейсу класса выглядит очень странно, не просто так вольности из дельфи не разрешаются в {$Mode objfpc}
trifon
постоялец
 
Сообщения: 135
Зарегистрирован: 24.12.2006 12:08:35

Сообщение Sergei I. Gorelkin » 30.10.2007 02:28:40

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

Собственно, а как еще можно вообще использовать CORBA-интерфейсы? В случае COM можно написать "MyObject as IMyInterface", или Supports(MyObject, IMyInterface, MyIntf). Но эти способы запрашивают интерфейс по GUID, который у CORBA отсутствует.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

След.

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

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

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

Рейтинг@Mail.ru