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

Вниз

Задачка :)   Найти похожие ветки 

 
Loginov Dmitry ©   (2006-11-04 11:49) [0]

Напишите реализацию следующей функции:

function ObjectExists(AObject: TObject; AClassType: TClass): Boolean;

Функция должна возвращать True, если AObject - актуальная ссылка на объект класса ClassType. Предусмотреть все варианты задания аргумента AObject.

Скажу сразу, задачка не для новичков.
Сам ее еще не решил :))


 
Anatoly Podgoretsky ©   (2006-11-04 11:57) [1]

> Loginov Dmitry  (04.11.2006 11:49:00)  [0]

Когда решишь в полном объеме, выдадим нобелевку


 
TUser ©   (2006-11-04 12:00) [2]

А когда такая задача вообще может возникнуть? Обычно люди предусмотрительно не работают с неактуальными ссылками, в крайнем случае (если актуальность действителньо неизвестна) защищаются блоком try.

В случае TComponent можно покопать в стороны ComponentState, хотя и там это не нужно, проще перекрывать Нотификейшн и следить за исчяезновением объекта.


 
Leonid Troyanovsky ©   (2006-11-04 12:05) [3]


> Loginov Dmitry ©   (04.11.06 11:49)  

> Скажу сразу, задачка не для новичков.


А профи разве такие решают?
Они, IMHO, сосредоточены на вечных двигателях.

--
Regards, LVT.


 
Kolan ©   (2006-11-04 12:05) [4]

Я начну, всеравно делать нечего :)

function ObjectExists(AObject: TObject; AClassType: TClass): Boolean;
begin
 Result := True;
 if AObject = nil then
   Result := False;
 else
   if not(AObject is AClassType) then
     Result := Fasle;
end;


 
Anatoly Podgoretsky ©   (2006-11-04 12:05) [5]

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


 
Anatoly Podgoretsky ©   (2006-11-04 12:07) [6]

Kolan ©   (04.11.06 12:05) [4]
Придется тебе выдать 50% нобелевки, за развитый вклад в науку объекто нахождения.


 
Kolan ©   (2006-11-04 12:07) [7]

> [6] Anatoly Podgoretsky ©   (04.11.06 12:07)
> Kolan ©   (04.11.06 12:05) [4]
> Придется тебе выдать 50% нобелевки, за развитый вклад в
> науку объекто нахождения.

Номер WMZ кошелька постить? :)


 
Leonid Troyanovsky ©   (2006-11-04 12:07) [8]


> TUser ©   (04.11.06 12:00) [2]

> А когда такая задача вообще может возникнуть? Обычно люди
> предусмотрительно не работают с неактуальными ссылками,
> в крайнем случае (если актуальность действителньо неизвестна)
> защищаются блоком try.


Начал за здравие, кончил - как всегда.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2006-11-04 12:14) [9]


> Kolan ©   (04.11.06 12:07) [7]

> Номер WMZ кошелька постить? :)


Лучше физические координаты, дабы
благодарная общественность могла лично, т.с.,
поблагодарить.

--
Regards, LVT.


 
Kolan ©   (2006-11-04 12:17) [10]

> могла лично, т.с.,
> поблагодарить.

Я так понял, что номера дома хватит?


 
Leonid Troyanovsky ©   (2006-11-04 12:21) [11]


> Kolan ©   (04.11.06 12:17) [10]

> Я так понял, что номера дома хватит?


Вполне, см. пример by Германн.

--
Regards, LVT.


 
Loginov Dmitry ©   (2006-11-04 14:00) [12]

Для собственных классов данная задача вполне решима. Достаточно добавить в класс булево поле. В конструкторе устанавливать это поле в True. В деструкторе сбрасывать в False. В функции ObjectExists() делать проверку этого поля.

А как решить подобную задачу в общем случае - фиг знает.


 
PEAKTOP ©   (2006-11-04 14:05) [13]

Программист Сидор Пентюхов пишет одну строчку кода за 30 секунд, а
программистка Клава Мышкина - за 3 минуты. При этом Пентюхов делает
одну ошибку на 15 строк, а Мышкина - 2 ошибки на 7 строк. По чьей вине
раньше повиснет программа заказчика, если Мышкина пишет на Дельфи, а
Пентюхов - на ассемблере?


 
Virgo_Style ©   (2006-11-04 14:08) [14]

Loginov Dmitry ©   (04.11.06 14:00) [12]

Я бы для собственных классов сделал массив указателей (*) на существующие объекты. Конструктор добавляет, деструктор - удаляет. А проверять поле несуществующего объекта... imho как-то рискованно и некрасиво.

(*) скорее всего, завернутый в класс.


 
PEAKTOP ©   (2006-11-04 14:10) [15]

Длина земного экватора L = 40 тысяч километров.
Ставим по всему экватору на равном расстоянии друг от друга 40 буев.
Сверяем часы. На первом буе вспышка на одну тысячную секунды, затем на втором буе вспышка на одну тысячную секунды и так далее.
В результате вспышка совершает полный круг по экватору за время t = 40*0.001 = 0.04 секунды. Инопланетяне видят огонек, бегающий по Земле со скоростью v = L / t = 40,000/0.04 = 1 миллион километров в секунду, что в три с лишним раза больше скорости света, и охреневают.


 
Loginov Dmitry ©   (2006-11-04 14:17) [16]

Virgo_Style ©   (04.11.06 14:08) [14]
А проверять поле несуществующего объекта... imho как-то рискованно и некрасиво.


На счет рискованно - врядли. Я сделал так:


function MatrixRefIsValid(AMatrix: TMatrix): Boolean;
begin
 Result := True;
 try
   if not AMatrix.FLifeFlag then
     Abort;

   if not (TObject(AMatrix) is TMatrix) then
     Abort;
 except
   Result := False;
 end;
end;


Подавал всевозможную хрень вместо TMatrix, работает стабильно и как надо.

Но. Мне не кажется это красивым. И смахивает на изврат :(


 
TUser ©   (2006-11-04 14:32) [17]

> Для собственных классов данная задача вполне решима. Достаточно
> добавить в класс булево поле. В конструкторе устанавливать
> это поле в True. В деструкторе сбрасывать в False. В функции
> ObjectExists() делать проверку этого поля.

Это далеко не обязано работать. Вот контрпример (компилировать в Delphi7).

type
TClass = class
 private
  FExist: boolean;
 public
  constructor Create;
  destructor Destroy; override;
  property Exist: boolean read FExist;
 end;

constructor TClass.Create;
begin
 inherited;
 FExist:=true;
end;

destructor TClass.Destroy;
begin
 FExist := false;
 inherited;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 with TClass.Create do begin
   Free;
   if Exist then showMessage ("1+") else ShowMessage ("1-");
   end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var p: pointer;
begin
 p := pointer (Random (MaxInt)) ;
 with TClass (p^) do
   if Exist then showMessage ("2+") else ShowMessage ("2-");
end;

procedure TForm1.Button3Click(Sender: TObject);
var O: TObject;
begin
 O := TStringList.Create;
 with TClass (O) do
   if Exist then showMessage ("3+") else ShowMessage ("3-");
end;


 
Leonid Troyanovsky ©   (2006-11-04 14:37) [18]


> Loginov Dmitry ©   (04.11.06 14:00) [12]

> Для собственных классов данная задача вполне решима.


Для компонентов, желающих хранить ссылки на другие
компоненты - задача вполне решаемая. См. [2] часть 2,
последняя строка.

FreeNotification also.

--
Regards, LVT.


 
Loginov Dmitry ©   (2006-11-04 14:58) [19]

TUser ©   (04.11.06 14:32) [17]
Это далеко не обязано работать. Вот контрпример (компилировать в Delphi7).


Да. В данном случае действительно моя теория обламывается.
Тогда не понимаю, почему мой код из [16] работает, и всегда возвращает правильный результат.


 
Loginov Dmitry ©   (2006-11-04 15:05) [20]

TUser ©   (04.11.06 14:32) [17]

Если перед полем FExist: boolean; вставить еще поле Integer, то Button1 выдает 1-. Похоже, что при уничтожении объекта содержимое первых четырех байтов портятся.


 
Alex Konshin ©   (2006-11-04 15:08) [21]

> Loginov Dmitry ©   (04.11.06 14:17) [16]
> На счет рискованно - врядли. Я сделал так:
> function MatrixRefIsValid(AMatrix: TMatrix): Boolean;
> begin
>  Result := True;
>  try
>    if not AMatrix.FLifeFlag then
>      Abort;
>
>    if not (TObject(AMatrix) is TMatrix) then
>      Abort;
>  except
>    Result := False;
>  end;
> end;

А кто тебе сказал, что указатель вообще на объект?
Вызови MatrixRefIsValid(TMatrix(Pointer(1)));

Даже если адрес объекта более-менее разумный (т.е. проверен VirtualQuery() и кратен 8), то все равно нет гарантии, что первое дв.слово - адрес какого-то VMT.
То есть, нельзя вызывать TObject(AMatrix) is TMatrix без предварительной проверки и этого адреса на разумность.
Действительно, единственный надежный подход - добавлять/удалять объекты в некий список во время AfterConstruction/BeforeDestruction. Если это нужно делать быстро и объектов много, то используй AVL деревья поиска.


 
TUser ©   (2006-11-04 15:32) [22]

> Loginov Dmitry ©   (04.11.06 14:58) [19]

Тут глобальная ошибка одна - использование каких-то недокументированных фичей. Компилятор может работать так, а может здак. Гарантировано лишь то, что обещано Борландом, остальное - от лукавого.


 
Бурундук ©   (2006-11-04 15:42) [23]

2Alex Konshin ©   (04.11.06 15:08) [21]
В принципе через vmtSelfPtr можно проверить, является ли
некоторое дв. слово указателем на VMT или нет.


 
palva ©   (2006-11-04 19:09) [24]

Loginov Dmitry ©   (04.11.06 14:00) [12]
Ну значит, доработать TObject и перекомпилировать System


 
DiamondShark ©   (2006-11-04 19:58) [25]


> А как решить подобную задачу в общем случае - фиг знает.

Менеджер памяти подменять.


 
Alex Konshin ©   (2006-11-04 23:06) [26]

> Бурундук ©   (04.11.06 15:42) [23]
> 2Alex Konshin ©   (04.11.06 15:08) [21]
> В принципе через vmtSelfPtr можно проверить, является ли
> некоторое дв. слово указателем на VMT или нет.

Сначала нужно убедиться, что это вообще доступная область памяти, чтобы обращение к нему не привело к AV.


 
Loginov Dmitry ©   (2006-11-04 23:58) [27]

Alex Konshin ©   (04.11.06 15:08) [21]
Действительно, единственный надежный подход - добавлять/удалять объекты в некий список во время AfterConstruction/BeforeDestruction


А что. Совет дельный. Пожалуй так и сделаю.
Просто действительно терпеть не могу программировать вещи сомнительного качества (особенно в общественно полезных разработках :)).


 
Loginov Dmitry ©   (2006-11-05 08:16) [28]

[27] - не прокатит. Не учтен случай, когда указатель на объект передается из exe в dll без использования пакетов.


 
Бурундук ©   (2006-11-05 10:35) [29]

2Alex Konshin ©   (04.11.06 23:06) [26]
>Сначала нужно убедиться, что это вообще доступная
>область памяти, чтобы обращение к нему не привело к AV.

Какие проблемы? Вроде IsBadReadPtr еще никто не отменял.
Её использование в данной ситуации казалось мне очевидным...


 
Anatoly Podgoretsky ©   (2006-11-05 12:07) [30]


> Вроде IsBadReadPtr еще никто не отменял.

Ты думаешь задача выявить будет AV или нет.
Задача состоит в том, что указатель актуальная ссылка на объект, если указатель содержал ссылку на A, а потом стал указывать на B, то ссылка эта не актуальна, хоть и указывает на объект того же типа, что мне делать с В? А уж про все прочии варианты и говорить не стоит.


 
Mystic ©   (2006-11-05 14:00) [31]

В менеджере памяти Delphi 7 для определения объекта используется следующий код:

procedure THeapBlockCollector.CollectObjects(HeapBlock: Pointer; AllocatedSize: Integer);
var
 AObject: TObject;
 AClass: TClass;
type
 PPointer = ^Pointer;
begin
 try
   if AllocatedSize < 4 then
     Exit;
   AObject := TObject(HeapBlock);
   AClass := AObject.ClassType;
   if (AClass = FClass)
     or (FFindDerived
       and (Integer(AClass) >= 64*1024)
       and (PPointer(PChar(AClass) + vmtSelfPtr)^ = Pointer(AClass))
       and (AObject is FClass)) then
   begin
     if FCount < Length(FObjectTable) then
       FObjectTable[FCount] := AObject;
     Inc(FCount);
   end;
 except
 //  Let"s not worry about this block - it"s obviously not a valid object
 end;
end;


Допущения: HeapBlock должен указывать на начало выделеного блока в динамической памяти. В принципе, если используется Delphi 7 и ниже, то можно по данному указателю проверить, является ли он указателем в Heap (прогуляться вперед назад и посмотреть, насколько валидны там данные). Но в любом случае никто не помешает нам сделать контрпример, который будет в памяти подстраиваться под все наши проверки. Так что 100% не получиться. Хотя вероятность случайного совпадения и невелика.


 
Alex Konshin ©   (2006-11-05 14:30) [32]

> Допущения: HeapBlock должен указывать на начало выделеного
> блока в динамической памяти. В принципе, если используется
> Delphi 7 и ниже, то можно по данному указателю проверить,
>  является ли он указателем в Heap (прогуляться вперед назад
> и посмотреть, насколько валидны там данные). Но в любом
> случае никто не помешает нам сделать контрпример, который
> будет в памяти подстраиваться под все наши проверки. Так
> что 100% не получиться. Хотя вероятность случайного совпадения
> и невелика.

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

И вы все забываете и таком простом доводе, что все эти проверки будут дорогими и все равно ненадежными. В плохих ситуациях будут нередки исключения, что дает накладные расходы, которые вроде как и незаметны. Если пытаться избежать исключения с помощью предварительных проверок, то уже будем тратить время на эти самые проверки. Я уж не говорю про случай многопоточного приложения, где надежность при таком подходе нельзя будет обеспечить в принципе, так как не с чем синхронизировать (в процессе вашей проверки ситуация может меняться независимо от вашего потока).

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


 
Mystic ©   (2006-11-05 14:52) [33]

> Alex Konshin ©   (05.11.06 14:30) [32]

Я же про это говорил, что надо дописать код, который определяет, валиден ли указатель в Heap (прогультяться по хипу). С учетом того, что по смещению -4  Heap хранит размер блока (Delphi 7, начиная с BDS 2006 все не так) и три флага: свободен ли блок, свободен ли предыдущий, является ли текущий блок заполнителем. Конечно, есть вариант, что после освобождения памяти было проведено объединения данного блока со смежным блоком, в этом случае я не уверен, что флаг caFree будет установлен. Но тогда можно полезть в конец нашего блока и найти информацию о следующем блоке Посмотреть, установлен ли у него флаг caPrevFree и т. д.

Приведенный мною метод вызывается из метода
function FindObjects(AClass: TClass; FindDerived: Boolean): TObjectArray;
который возвращает массив всех классов, порожденных от TObjectArray. Чтобы получить доступ к этому методу, надо перекомпилировать System.pas (опция -y для dcc32) с установленым символом препроцесора DEBUG_FUNCTIONS.


 
Ketmar ©   (2006-11-05 15:30) [34]

вообще-то задача решается без кода. на уровне документации, где написано примерно такое:
dumb mode on: НЕ НАДО ПЕРЕДАВАТЬ В ПРОЦЕДУРУ НЕКОРРЕКТНЫЕ УКАЗАТЕЛИ! dumb mode off.


 
Суслик ©   (2006-11-05 15:34) [35]

Примерную задачу решает FastMM - манагер памяти.
Но он ее решает следующим образом - при создании и удалении объектов записывает служебную инфу. Если попытаться вызывать *виртуальный* метод удаленного объекта, то он честно расскажет когда объект был создан, когда удален и какой стек вызовов привел к некорректному вызову удаленного объекта

PS.
Есно это все делается, если укажешь соотв. опцию - такая фича израядно тормозит и посему может использоваться исключительно для отладки.


 
Суслик ©   (2006-11-05 15:35) [36]

Моем имхо такое - если не влезать во внутренности создания/удаления объектов - написать такую функцию невозможно.


 
Loginov Dmitry ©   (2006-11-05 17:22) [37]

Ketmar ©   (05.11.06 15:30) [34]
вообще-то задача решается без кода. на уровне документации, где написано примерно такое:
dumb mode on: НЕ НАДО ПЕРЕДАВАТЬ В ПРОЦЕДУРУ НЕКОРРЕКТНЫЕ УКАЗАТЕЛИ! dumb mode off.


В обычных задачах так оно и есть. Но вот заело. Хочу, чтоб функции Matrix32 по возможности контроллировали актуальность передаваемых ссылок, и вместо непонятных сообщений вида "Access violation at address xxxxxxxxx. Read of address xxxxxxxxx". Выдавались внятные сообщения, типа:
"При работе функции "ххххххххх" произошла ошибка с текстом "Ссылка не является массивом TMatrix".
Отлаживать такие программы проще.

Ксате, написал такой вариант:

function MatrixRefIsValid(AMatrix: TMatrix): Boolean;
begin
 Result := True;
 try
   if not IsEqualGUID(AMatrix.FLifeGUID, LifeMatrixGuid) or
     not (TObject(AMatrix) is TMatrix)
   then
     Result := False;  
 except
   Result := False;
 end;
end;


Гуид-поле заполняется в конструкторе, обнуляется в деструкторе. Надежность должна быть не менее 99%.


 
Суслик ©   (2006-11-05 17:33) [38]


> Гуид-поле заполняется в конструкторе, обнуляется в деструкторе.
>  Надежность должна быть не менее 99%.

очень, очень, очень, очень зависит от манагера памяти :)
манагеры они веднь разные.
в bds2006 и далее идет саааавсем не тот манагер, который был раньше.
просто другая разработка.

да даже и со старым манагером памяти сильные меня сомнению берут по поводу надежности в 99 процентов.


 
Ketmar ©   (2006-11-05 17:36) [39]

>[37] Loginov Dmitry(c) 5-Nov-2006, 17:22
>Отлаживать такие программы проще.
проще такие не писать.


 
Суслик ©   (2006-11-05 17:37) [40]

для отладки, уважаемый автор, есть другие средства. одно из них я уже упомянул выше.



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

Форум: "Прочее";
Текущий архив: 2006.11.26;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.58 MB
Время: 0.043 c
11-1137494056
Grom PE
2006-01-17 13:34
2006.11.26
Странности KOL и MCK (v2.31)


2-1161856009
Khabibulin
2006-10-26 13:46
2006.11.26
TCalendar


4-1152879465
Acidlex
2006-07-14 16:17
2006.11.26
Эмуляция нажатия клавиш в чужом окне


4-1152738077
GanibalLector
2006-07-13 01:01
2006.11.26
Тип винчестера


15-1162969180
Lexer
2006-11-08 09:59
2006.11.26
Распространение групповой политики на весь домен





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский