Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2007.06.03;
Скачать: [xml.tar.bz2];

Вниз

Очередные баги компилятора.   Найти похожие ветки 

 
oxffff ©   (2007-04-09 15:41) [0]

Сегодня рассмотрим const передачу интерфейсов

Простой пример

procedure abc(const a:Iunknown);
var b:Iunknown;
begin
b:=a;
b:=nil;
end;

procedure TForm1.Button1Click(Sender: TObject);
var a:TInterfacedObject;
begin
a:=TInterfacedObject.Create;
abc(a);
// a уже разрушен
end;

Здесь после вызова  abc объект а уже разрушен.

Аналогичный рабочий пример используя временную переменную

procedure TForm1.Button1Click(Sender: TObject);
var a:TInterfacedObject;
begin
a:=TInterfacedObject.Create;
abc(a as IUnknown);
// a еще жив
end;

Здесь после вызова  abc объект a еще неразрушен.

Выводы.

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

Выявленная ошибка работает на D7, BDS 2006 SP2


 
tesseract ©   (2007-04-09 16:03) [1]


> Компилятор нужно поправить, чтобы при передачи const интерфейса
> нужно создавать временную переменную,


Объекты вроде всегда по ссылке передавались. Они не являються простым типом данных.


 
oxffff ©   (2007-04-09 16:06) [2]


> Объекты вроде всегда по ссылке передавались. Они не являються
> простым типом данных.


Здесь при передачи переменная а приводится Iunknown.


 
clickmaker ©   (2007-04-09 16:15) [3]


> procedure abc(const a:Iunknown);
> var b:Iunknown;
> begin
> b:=a;
> b:=nil;
> end;

** не смог представить случай, когда такой изврат может понадобиться **


 
Джо ©   (2007-04-09 16:20) [4]

> Здесь после вызова  abc объект a еще неразрушен.


type
 TMyInterfacedObject = class (TInterfacedObject, IUnknown)
 public
   destructor Destroy; override;  
 end;

implementation

{ TMyInterfacedObject }

destructor TMyInterfacedObject.Destroy;
begin
 ShowMessage ("Destroyed");
 inherited;
end;

procedure abc(const a:Iunknown);
var
 b:IUnknown;
begin
 b := a;
 b := nil;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 a: TMyInterfacedObject;
begin
 a:=TMyInterfacedObject.Create;
 abc(a as IUnknown);
 // Разрушается
end;

end.


D7, все корректно разрушается.


 
oxffff ©   (2007-04-09 16:21) [5]


> ** не смог представить случай, когда такой изврат может
> понадобиться **


Ну это собственно пример.

Процедура abc может копировать, запрашивать интерфейсы вынуждая вызовы _addref, _Release, а может этого не делать.

Поэтому в первом случае
abc(А) нельзя гарантровано сказать жив объект А, или мерт.

Во втором случае
abc(А as Iunknown)
А жив всегда после возврата из abc.


 
oxffff ©   (2007-04-09 16:23) [6]


> Джо ©   (09.04.07 16:20) [4]
> > Здесь после вызова  abc объект a еще неразрушен.
>
>
> type
>  TMyInterfacedObject = class (TInterfacedObject, IUnknown)
>  public
>    destructor Destroy; override;  
>  end;
>
> implementation
>
> { TMyInterfacedObject }
>
> destructor TMyInterfacedObject.Destroy;
> begin
>  ShowMessage ("Destroyed");
>  inherited;
> end;
>
> procedure abc(const a:Iunknown);
> var
>  b:IUnknown;
> begin
>  b := a;
>  b := nil;
> end;
>
> procedure TForm1.Button1Click(Sender: TObject);
> var
>  a: TMyInterfacedObject;
> begin
>  a:=TMyInterfacedObject.Create;
>  abc(a as IUnknown);
>  // Разрушается
> end;
>
> end.
>
>
> D7, все корректно разрушается.


Конечно все разрушается, но уже финализатором.


А я про момент тогда мы еще не дошли до финализатора.


 
oxffff ©   (2007-04-09 16:28) [7]

Еще раз  

procedure TForm1.Button1Click(Sender: TObject);
var a:TInterfacedObject;
begin
a:=TInterfacedObject.Create;
abc(a);

a.someProc //приведет к сбою в случае если abc работает со счетчиком ссылок. Но это установить нельзя по причине того, что abc может быть произвольной

end;

procedure TForm1.Button1Click(Sender: TObject);
var a:TInterfacedObject;
begin
a:=TInterfacedObject.Create;
abc(a as Inknown);

a.someProc // обработает нормально

end;

Понятно?

Конечно можно сделать так, чтобы гарантировано после abc А был жив

procedure TForm1.Button1Click(Sender: TObject);
var a:TInterfacedObject;
    b:iunknown;
begin
a:=TInterfacedObject.Create;
b:=a;
abc(a);
end;


Но хочеться чтобы это делал компилятор.


 
SlymRO ©   (2007-04-09 16:28) [8]

Джо ©   (09.04.07 16:20) [4]
abc(a as IUnknown);

От тута временная переменная (накрутка счетчика)
abc(a); - так накрутки не происходит, но происходит декрутка :) внутри abc
сей баг аналогичен этому:
var
 a:TInterfacedObject;
 b:IUnknown;
begin
 a:=TInterfacedObject.Create;
 try
   b:=a as IUnknown;
   b:=nil;
 finally
   a.Free;
 end;
end;


 
Джо ©   (2007-04-09 16:29) [9]

А, не понял сначала, об чем речь, сорри.


 
jack128 ©   (2007-04-09 16:32) [10]

ИМХО, это НЕ баг.


 
oxffff ©   (2007-04-09 16:37) [11]

Вот пример

TSomeClass=class(TinterfacedObject)
protected
SomeText:string;
public
procedure Myproc;virtual;
constructor create;
end;

procedure abc(const a:Iunknown);
var b:Iunknown;
begin
b:=a;
b:=nil;
end;

constructor TSomeClass.create;
begin
inherited;
SomeText:="THIS STRING REFERENCE WILL BE EMPTY AFTER DESTRUCTION";
end;

procedure TSomeClass.Myproc;
begin
showmessage(SomeText);
end;

procedure TForm1.Button1Click(Sender: TObject);
var a:TSomeClass;
begin
a:=TSomeClass.Create;
abc(a);
a.Myproc;
//А После этого уже нет
end;


 
oxffff ©   (2007-04-09 16:43) [12]


> Джо ©   (09.04.07 16:29) [9]
> А, не понял сначала, об чем речь, сорри.


То есть при приведении объекта к интерфейсу, нужно создавать временную
переменную, чтобы гарантировано продлить жизнь объекту.
Компилятор использует внутрениие поля объекта. Поэтому установить состояние объекта после возвращение из функции невозможно.
Поскольку он уже может быть разрушен, а может и нет.


 
oxffff ©   (2007-04-09 16:45) [13]


> SlymRO ©   (09.04.07 16:28) [8]
> Джо ©   (09.04.07 16:20) [4]
> abc(a as IUnknown);
> От тута временная переменная (накрутка счетчика)
> abc(a); - так накрутки не происходит, но происходит декрутка
> :) внутри abc
> сей баг аналогичен этому:
> var
>  a:TInterfacedObject;
>  b:IUnknown;
> begin
>  a:=TInterfacedObject.Create;
>  try
>    b:=a as IUnknown;
>    b:=nil;
>  finally
>    a.Free;
>  end;
> end;


Сей пример неудачен. Это не аналогия.


 
oxffff ©   (2007-04-09 16:51) [14]


>
> Сей пример неудачен. Это не аналогия.


Ну если в контексте
То есть после возврата из abc(она может быть произвольной)
при вызове
abc(A)

нельзя гарантированно сказать надо делать A.Free или нет;


 
SlymRO ©   (2007-04-09 16:53) [15]

oxffff ©   (09.04.07 16:45) [13]
Это не аналогия

Чем не аналогия? Теже симптомы, в томже анальном месте...


 
oxffff ©   (2007-04-09 16:56) [16]


> SlymRO ©   (09.04.07 16:53) [15]
> oxffff ©   (09.04.07 16:45) [13]
> Это не аналогия
> Чем не аналогия? Теже симптомы, в томже анальном месте..
> .


В моих примерах о A.FREE не было речи.

посмотри мои примеры выше и пример oxffff ©   (09.04.07 16:37) [11]


 
oxffff ©   (2007-04-09 17:02) [17]


> SlymRO ©   (09.04.07 16:53) [15]
> oxffff ©   (09.04.07 16:45) [13]
> Это не аналогия
> Чем не аналогия? Теже симптомы, в томже анальном месте..
> .


Ты согласен, что это баг компилятора?


 
Сергей М. ©   (2007-04-09 17:23) [18]


> Компилятор нужно поправить, чтобы при передачи const интерфейса
> нужно создавать временную переменную


Не надо ничего "подправлять".

То что ты называешь "временной переменной", и так уже создается при передаче параметра без const-модификатора. И поведение компилятора при этом отличается - им вставляются неявные _AddRef/_Release


 
oxffff ©   (2007-04-09 17:59) [19]


> Не надо ничего "подправлять".
>
> То что ты называешь "временной переменной", и так уже создается
> при передаче параметра без const-модификатора. И поведение
> компилятора при этом отличается - им вставляются неявные
> _AddRef/_Release


Перечитайте пожалуйста сначала
Чтобы не копировать ссылки, передавайте интерфейс как const параметр.
Компилятор в этом случае не вставляет _AddRef/_Release.


 
Сергей М. ©   (2007-04-10 08:15) [20]


> Чтобы не копировать ссылки, передавайте интерфейс как const
> параметр


const никак не относится к копированию или некопированию ссылок.

Что с const, что без const - никакие ссылки, передаваемые в кач-ве факт.параметров,  не копируются.


> Компилятор в этом случае не вставляет _AddRef/_Release.


Да. И это единственное отличие от случая передачи без const.


 
_Аноним   (2007-04-10 10:07) [21]

Об этом вроде говорено уже было производителем, что не надо так делать.
Так что пожалуй таки не баг.


 
homm ©   (2007-04-10 10:36) [22]

> Об этом вроде говорено уже было производителем, что не надо так делать.
> Так что пожалуй таки не баг.

Баг, о котором сказал производитель — не баг? А приступление в котором признался приступник — уже не приступление?


 
oxffff ©   (2007-04-10 10:58) [23]


> Сергей М. ©   (10.04.07 08:15) [20]
>
> > Чтобы не копировать ссылки, передавайте интерфейс как
> const
> > параметр
>
>
> const никак не относится к копированию или некопированию
> ссылок.
>
> Что с const, что без const - никакие ссылки, передаваемые
> в кач-ве факт.параметров,  не копируются.


Как это не копируются?

При передачи интерфейса копирование в случае передачи по значению
инициируется не caller, а called фукнцией.

При передачи интерфейса копирование в случае const передачи не происходит.


 
oxffff ©   (2007-04-10 11:02) [24]

to Сергей М

Откройте отладчик. И Посмотрите разницу

Здесь два копирования.

procedure abc(a:Iunknown);
var b:Iunknown;
begin
b:=a;
b:=nil;
end;

А здесь одно копирование.

procedure abc(Const a:Iunknown);
var b:Iunknown;
begin
b:=a;
b:=nil;
end;


 
Сергей М. ©   (2007-04-10 11:13) [25]


> oxffff ©   (10.04.07 11:02) [24]


> Здесь два копирования


Что ты подразумеваешь под "копированием" ?
Вызов call @IntfCopy что ли ?


 
oxffff ©   (2007-04-10 11:20) [26]


> Сергей М. ©   (10.04.07 11:13) [25]
>
> > oxffff ©   (10.04.07 11:02) [24]
>
>
> > Здесь два копирования
>
>
> Что ты подразумеваешь под "копированием" ?
> Вызов call @IntfCopy что ли ?


Вызов @IntfCopy - это копирование.

Если вы внимательно посмотрите, то чушь выше будут такие конструкции

Mov [ebp-$04],eax
..
Call @intfAddref

И это тоже копирование.


 
oxffff ©   (2007-04-10 11:24) [27]


> Что ты подразумеваешь под "копированием" ?


Все что приводит к инициированию вызова _Addref.


 
Сергей М. ©   (2007-04-10 11:28) [28]


> Вызов @IntfCopy - это копирование
>Call @intfAddref
>И это тоже копирование.


Это самое "копирование" есть ничто иное как инкремент сч-ка ссылок, не более того.


> Mov [ebp-$04],eax


А вот это действительно копирование.


 
Сергей М. ©   (2007-04-10 11:34) [29]


> Все что приводит к инициированию вызова _Addref.


Хм ...

Явный вызов GetInterface, по-твоему, тоже копирование ?
Интересная трактовка)

Я вообще до сих пор не пойму, что же ты собрался "править" ?

По-моему, действия компилятора для случая с const и без него вполне прозрачны и оправданы.


 
oxffff ©   (2007-04-10 11:35) [30]


> Сергей М. ©   (10.04.07 11:28) [28]
>
> > Вызов @IntfCopy - это копирование
> >Call @intfAddref
> >И это тоже копирование.
>
>
> Это самое "копирование" есть ничто иное как инкремент сч-
> ка ссылок, не более того.
>
>
> > Mov [ebp-$04],eax
>
>
> А вот это действительно копирование.


Вы странно рассуждаете.

@IntfCopy - производит копирование указателя и увеличение счетчика

2. Mov [ebp-$04],eax
  Call @intfAddref

производит копирование указателя и увеличение счетчика.
Такова Семантика копирования интерфейсов.

Почему вы подразумеваете одно без другого?

>Что с const, что без const - никакие ссылки, передаваемые в кач-ве >факт.параметров,  не копируются.

Это утверждение высказанное вами не верно.


 
oxffff ©   (2007-04-10 11:47) [31]


> Явный вызов GetInterface, по-твоему, тоже копирование ?


А что нет что ли?

Pointer(Obj) := Pointer(Integer(Self) + InterfaceEntry^.IOffset);
if Pointer(Obj) <> nil then IInterface(Obj)._AddRef;

или тоже @IntfCopy при

IInterface(Obj) := InvokeImplGetter(Self, InterfaceEntry^.ImplGetter);


> Я вообще до сих пор не пойму, что же ты собрался "править"
> ?


Перечитайте еще раз.

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

То есть, если есть функция

procedure abc(const a:iinterface);
var b:iinterface
begin
..
end;

Есть переменная c:TinterfacedObject

И мы делаем такой вызов  abc(c)

То после нельзя однозначно сказать жив объект или нет после возврата из abc. Поскольку все зависит от работы функции abc, происходит ли копирование или нет.

Поэтому нужно при передачи caller должен создавать временную переменную.


 
oxffff ©   (2007-04-10 11:51) [32]


> _Аноним   (10.04.07 10:07) [21]
> Об этом вроде говорено уже было производителем, что не надо
> так делать.
> Так что пожалуй таки не баг.


Где?
Если не затруднит ссылочку. Пожалуйста.
Заранее благодарен


 
Сергей М. ©   (2007-04-10 12:03) [33]


> То после нельзя однозначно сказать жив объект или нет после
> возврата из abc


Да хоть правь, хоть не правь ты компилятор - однозначного ответа все равно не будет.

Ибо в теле abc() я волен вызать метод _Release у любой доступной мне "копии" столько раз, сколько мне заблагорассудится, и тем самым разрушить объект, не смотря на кучу переменных (явных или неявных), ссылающихся на интерфейс объекта.



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2007.06.03;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.55 MB
Время: 0.038 c
2-1179225218
Lakshmy
2007-05-15 14:33
2007.06.03
Показ (0,0) угла TreeView


15-1178614834
Juice
2007-05-08 13:00
2007.06.03
QunatumGrid


15-1178362347
ArtemESC
2007-05-05 14:52
2007.06.03
Шахматы...


15-1178805576
Magedon
2007-05-10 17:59
2007.06.03
Почему на форуме нет возможности превью написанного сообщения?


2-1178972587
SKIPtr
2007-05-12 16:23
2007.06.03
Как запустить процедуру





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский