![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Недомолвка в FAQ насчет AncestorOfObject [Delphi, Windows]
BenGun © (2006-03-06 18:55:00)>> В KOL нет RTTI, поскольку используется "старая" ООП-модуль (не class, а object). В итоге вместо
>> MySuper as TSmth пишем PSmth(MySuper), а вместо MySuper is TSmth пишем TSmth.AncestorOfObject(MySuper)
разве не стоит указать, что AncestorOfObject возможно использовать только в методах объекта, являющегося потомком TSmith или самого TSmith?
ECM (2006-03-10 09:27:00) [1]
> GMax (09.03.06 23:48) [17]
Согласен, но это уже конкретизация экземпляра класса(объекта), а речь выше шла о идентификации класса объекта (типа) - поэтому в случае осмысленных имен - придется делать их парсинг, что, вобщем,является более "дорогостоящей" операцией, чем сравнение двух целых чисел
:))
GMax (2006-03-09 23:48:00) [2]а ещё можно USE_NAMES и давать осмысленные имена :)
ECM (2006-03-09 18:23:00) [3]
> z007 (07.03.06 21:20) [12]
> Если это есть самопальные типы, ну сделай в них поле / property
> Barloggg (09.03.06 09:21) [14]
> помню как-то сбацал "главного предка" который был простым
> наследником с одним единственным дополнительным полем
> BenGun (07.03.06 18:42) [9]
> для конкретики: бог с ними, с control'ами, что насчет наследников
> TObj?
Кстати, для этих целей вполне можно использовать поле TObj.Tag. В конструкторе потомка заносить туда "уникальный" номер (для TObj это будет 0). Потом анализировать ...
ECM (2006-03-09 13:01:00) [4]Я вижу ясности в главном вопросе об AncestorOfObject, так и не прибавилось... Поэтому приведу тут свои соображения (решил посидеть пару часов - разобраться что к чему) - если я где-то ошибаюсь - пожалуйста поправьте. Вообще-то я считал, что в таких базовых вопросах
уже давно всё в порядке (сам я этой функцией не пользуюсь) - ну да ладно
смотрим (у меня D6 и KOL 2.34):
class function TObj.AncestorOfObject(Obj: Pointer): Boolean;
asm
MOV ECX, [EAX]
// EAX на входе функции равен Self и следовательно в ECX попадает первый
// DWORD из памяти распределенной под объект. А как можно увидеть из
// ассемблерного кода функции TypeOf там лежит указатель на VMT TObj
// Эксперимент с вызовом функции напрямую (без создания
// экземпляра класса) показывает что в EAX передается указатель на некую
// ячейку памяти в которой тоже расположен указатель на VMT TObj
// Итого: ECX => VMT TObj
MOV EAX, EDX
// EAX => указатель на проверяемую переменную
JMP @@loop1
@@loop:
MOV EAX,[EAX]
//
@@loop1:
TEST EAX,EAX
//Проверка на ноль
JE @@exit
CMP EAX,ECX
// В первом же цикле сравнивается указатель на VMT с указателем на
// переменную!!!! (Предполагалось я думаю сравнивать указатели на VMT)
// Если бы во второй команде функции было MOV EAX,[EDX] то в первом
// цикле так бы и случилось....но да ладно потом выполняется
// MOV EAX,[EAX] и в EAX уже VMT проверяемого объекта - если это
// VMT TObj то во втором цикле проверки - всё закончится, а если нет?
// см. ниже
JNE @@loop
@@success:
MOV AL,1
@@exit:
end;
Если во втором цикле проверка не проходит - в EAX загружается новое значение - теперь оно берется ПРЯМО ИЗ VMT! Т.к. далее сравниваться оно будет с VMT Tobj - остается предположить что в первой ячейке VMT автор функции ждет найти указатель на родительскую (?) VMT... и тогда цикл будет продолжен до совпадения либо до корневого класса где указатель на родительскую VMT = 0 ... ?.... Что то я не припомню, чтобы VMT для старой объектной модели были указатели на предков. Ну да это несложно проверить. Создаем в программе такие классы (например как в [10]):
type
PA = ^TA;
TA = object (TObj)
destructor Destroy; virtual;
end;
PAA = ^TAA;
TAA = object(TA)
end;
PAB = ^TAB;
TAB = object(TA)
end;
PTT = ^TT;
TT = class
destructor Destroy; virtual;
end;
После тщательного изучения кода получилась следующая картина - компилятор сгенерировал 4-е VMT расположенные в начале сегмента модуля. Я изучил их структуру и вот что у меня получилось(дамп пямяти):
Address | Value | Desc
--------+-----------+------------------------------------
$406F2C | $00406F38 | (vmtSelfPtr ) | VMT
$406F30 | $00000018 | (vmtInstanceSize) | TA = object(TObj)
$406F34 | $00000000 | (vmtPtrOffs ) |
$406F38 | $00403EF0 | _TObj.Init |
$406F3C | $004074D4 | TA.Destroy |
--------+-----------+------------------------------------
$406F40 | $00406F4C | (vmtSelfPtr ) | VMT
$406F44 | $00000018 | (vmtInstanceSize) | TAA = object(TA)
$406F48 | $00000000 | (vmtPtrOffs ) |
$406F4C | $00403EF0 | _TObj.Init |
$406F50 | $004074D4 | TA.Destroy |
--------+-----------+------------------------------------
$406F54 | $00406F60 | (vmtSelfPtr ) | VMT
$406F58 | $00000018 | (vmtInstanceSize) | TAB = object(TA)
$406F5C | $00000000 | (vmtPtrOffs ) |
$406F60 | $00403EF0 | _TObj.Init |
$406F64 | $004074D4 | TA.Destroy |
--------+-----------+------------------------------------
$406F68 | $00406FB4 | (vmtSelfPtr ) | VMT
$406F6C | $00000000 | (vmtIntfTable ) | TT = class
$406F70 | $00000000 | (vmtAutoTable ) |
$406F74 | $00000000 | (vmtInitTable ) |
$406F78 | $00000000 | (vmtTypeInfo ) |
$406F7C | $00000000 | (vmtFieldTable ) |
$406F80 | $00000000 | (vmtMethodTable ) |
$406F84 | $00000000 | (vmtDinamicTable) |
$406F88 | $00406FB8 | (vmtClassName ) |
$406F8C | $00000004 | (vmtInstanceSize) |
$406F90 | $00401000 | (vmtParent) VMT TObject
$406F94 | $004028B0 | TObject.SafeCallException
$406F98 | $004028BC | TObject.AfterConstruction
$406F9C | $004028C0 | TObject.BeforeDestruction
$406FA0 | $004028C4 | TObject.Dispath
$406FA4 | $004028B8 | TObject.DefaultHandler
$406FA8 | $00402794 | TObject.NewInstance
$406FAC | $004027B0 | TObject.FreeInstance
$406FB0 | $004027EC | TObject.Destroy
$406FB4 | $004074EC | TT.Destroy
$406FB8 | $90545402 | #90'TT'#02 (ClassName)
vmtSelfPtr (VMT - 12)- указатель на собственно VMT. Delphi резервирует (и как бы скрывает от программиста) первые служебные ячейки VMT. Значение как
можно заметить - указывает на начало собственно таблицы виртуальных
метедов (т.е. на первый виртуальный метод). Имеено значение передается
записывается в первую ячейку памяти выделенной под объект.
vmtInstanceSize (VMT - 8)- размер памяти который необходимо выделить для размещения объекта. Равен сумме размеров полей объекта + 4 (для хранения указателя на VMT). Именно это значение возвращается по SizeOf для объекта.
vmtPtrOffs (VMT - 4). Смещение в памяти выделенной под объект, где лежит указатель на VMT.(Это видно при анализе System.ObjSetup) Как видно это значение всегда (?) нулевое. Т.е. указатель на VMT всегда (?) расположен в первой ячейке памяти объекта (т.е. первое и неявное поле типа Pointer)
Как видно в VMT для объектной модели - нет никаких признаков наличия
указателя на VMT предка. И следовательно определить способом предлагаемым в AncestorOfObject кто есть предок просто невозможно!
Да и честно говоря - способа это сделать я не вижу. Сравнение кода System.pas для других версий Delphi показывает что структура VMT для
старой объектной модели везде (незнаю только что в D2, но скорее всего и там тоже) одинакова.....:(
...Да и еще - использование этой функции в конце-концов может привести к AV. Т.к на третьем этапе в AX попадает указатель на первый VM объекта,
на четвертом - строится указатель из первых 4 байт кода первого VM и дальше катится неконтроллируемо (хорошо если по пути случайно попадется нулевая ячейка) - и (как было в моем случае) падает с AV.
Может эта функция была написана для FPC и там в VMT есть указатели на VMT предка - не знаю...
Если есть кто из авторов этой функции ? Может кто пояснит ситуацию?
З.Ы. Кстати из дампа VMT для TT видно, что объявления
TT = class
...
end;
TT = class(TObject)
...
end;
это действительно одно и тоже ... :))
Barloggg (2006-03-09 09:21:00) [5]хм... все свои классы я делаю именно class'ами. никто не запрещает. да и стоит это не дорого. где-то 3-5 кб ехе-шника.
А KOL использую только для компактных окошек и контролов.
А там где мне не нужно многократное наследование или я точно знаю какой где тип то тогда использую TObj.
еще из первых уроков которые я прочел прежде чем решиться вообще скачивать и ставить себе KOL+MCK мне стало ясно, что вся мощь class тут не используется и рассчитывать на 100% альтернативу бесполезно.
мне кстати недавно понадобилось свалить в одну кучу с пяток наследников от TObj разных типов... и я сбацал "оглавление" с жестко размеченными индексами для каждого объекта которое представляет собой динамический массив of PObj.
помню как-то сбацал "главного предка" который был простым наследником с одним единственным дополнительным полем - именем текущего класса. и в конструкторе идет заполнение этого поля текущим именем... а все остальные классы наследовал от него...
BenGun (2006-03-09 08:05:00) [6]:) это-то понятно. Если уж на то пошло, можно обойтись и процедурным программированием (даже блочным копированием! процедуры - буржуазная роскошь!) Речь идет о трудоёмкости. Вообще говоря, я хотел высказать пожелание подправить FAQ (втайне надеясь, что я чего-то недопонял и механизм существует). Спасибо всем, принявшим участие и авторам KOL особенно. Повторюсь: труд потрясает масштабами(!!!) и не юзать ТАКОЕ - просто преступление перед человечеством :)))))
z007 (2006-03-07 21:20:00) [7]Проблема знакомая. Сам с ней столкнулся при написании компонентов ( http://www.delphimaster.ru/cgi-bin/forum.pl?id=1140304918&n=10).
Однако:
Если это есть самопальные типы, ну сделай в них поле / property
(ftypeID:byte|word|integer|string, наконец). Как правило, без проверки типов действительно можно обойтись, в т.ч при создании собственных компонентов. Надо только врубиться в "правила KOL".
BenGun (2006-03-07 18:56:00) [8]хехе. ща бы меня тут волхвы похоронили :))) Оговорюсь и поправлю код (если успел)
program
(...)
var
A: PA;
begin
A := NewXXX; (вирианты A, AA, AB)
if (проверка A is TA) then
begin
if (проверка A is TAA) then
Do2
else
if (проверка A is TAB) then
Do3;
end;
end;
BenGun (2006-03-07 18:51:00) [9]даже так, еще конкретнее. Как мне развести следующее:
имеются типы
TAA->TA->TObj
TAB->TA->TObj
задача:
program
(...)
var
A: PA;
begin
A := NewXXX; (вирианты A, AA, AB)
if (проверка A is TA) then
Do1
else
if (проверка A is TAA) then
Do2
else
if (проверка A is TAB) then
Do3;
end;
BenGun (2006-03-07 18:42:00) [10]для конкретики: бог с ними, с control'ами, что насчет наследников TObj?
BenGun (2006-03-07 18:32:00) [11]Увы, ассемблер для меня - черный ящик. В Kol.pas смотрю прямо сейчас (снимаю шляпу перед автором, объем работы проделан чудовищный). Вижу, что все PControl есть именно PControl и тут, видимо, подразумевается иной механизм (скорее всего сравнение с каким-нибудь classname).
И всё-таки хотелось бы услышать чего-нибудь по существу вопроса :)
Вопрос стоит: "Как же быть?" :)
ECM (2006-03-07 18:07:00) [12]
> Не могли бы Вы поконкретнее?
Конкретнее не бывает (см. KOL.PAS):
class function TObj.AncestorOfObject(Obj: Pointer): Boolean;
asm
MOV ECX, [EAX]
MOV EAX, EDX
JMP @@loop1
@@loop:
MOV EAX,[EAX]
@@loop1:
TEST EAX,EAX
JE @@exit
CMP EAX,ECX
JNE @@loop
@@success:
MOV AL,1
@@exit:
end;
Смотрим, запускаем для разных классов, изучаем в отладчике... Или это надо сделать мне? :))
> С TControl'ами сейчас проверю
Это - туда же (см. KOL.PAS) :)))
З.Ы. Флейм про "лешего с гуЁм" - продолжать не стоит - не надо :))
(Вот, не хотел же отвечать на ... )
BenGun (2006-03-07 17:42:00) [13]to ECM
Уважаемый, безусловно меня в первую очередь интересует построение собственных классов и дальнейшее их использование(!!!). Паскаль можно задействовать не только для построения гуёв: для меня важнен комплекс - и гуй, и математика. Насчет Вашего "свой механизм" - я вижу только вариант, при котором мне надо будет создавать экземпляр объекта проверяемого типа.
>> "если нет сил (умения ) сделать свой - посмотрите что не так в том что есть... Это не так уж сложно" Не могли бы Вы поконкретнее? Очень похоже на флейм типа "ДА ТЫ НЕ НА ТОМ ЯЗЫКЕ ПИШЕШЬ!!"
С TControl'ами сейчас проверю (хотя это частная задача, имеющая отношение к проблемме весьма второстепенное)
ECM (2006-03-07 17:14:00) [14]
> не иметь возможности проверить экземпляр объекта на принадлежность
> к типу - это даже не смешно, это катастрофа! нет, это просто
> кошмар
Между прочим в КОЛ 99% контролов это TControl. И "вообще поменьше типов" - один из постулатов КОЛ. И "какого лешего"? :))
Если это Ваши типы - никто не мешает сделать свой механизм.. Если нет сил (умения ) сделать свой - посмотрите что не так в том что есть... Это не так уж сложно. (Важно то что это нужно именно Вам)
Но учтите - разницу между ComboBox-ом и ListBox-ом например (да и всеми остальными "стандартными" контролами ) - Вы не обнаружите - это всё TControl
BenGun (2006-03-07 16:16:00) [15]to Vladimir Kladov
работает в указанной области видимости (еще кое где, но там глючит)
to Thaddy
I always can do before call AncestorOfObject to check for nil and it's not a problem:)
Мужики, дело такое... В ООП да не иметь возможности проверить экземпляр объекта на принадлежность к типу - это даже не смешно, это катастрофа! нет, это просто кошмар! :( Насчет "мне он как-то не был нужен" я смею сомневаться в искренности.
P.S.
KOL - очень добротный проект, и мне хотелось бы его использовать, но без вышеупомянутой проверки это нереально, ибо
1. Привык
2. Это очень удобный механизм и неотемлемая часть ООП! Какого лешего?!
Огромное спасибо за участие (если честно, то не надеялся)
ICQ 111326219(hidden/always offline for "not in contacts")
email bgun@list.ru
Thaddy (2006-03-06 20:35:00) [16]Yes, but you know that generally is not good enough:
and obviously, the value of AncestorOfObject is undefined, not nil! when it fails.
Therefore, it isn't very good code (although generally it works ;) ) It might have been in the late 10th and early 20th century. :)
Vladimir Kladov (2006-03-06 19:52:00) [17]А он вообще работает? А то он долго не пахал, потом кто-то фиксил, а мне он как-то не был нужен, я и не смотрел, работает ли он вообще.
Thaddy (2006-03-06 19:43:00) [18]Yup, this code is verifying, not falsifying (i.e: it does not test for impossible options, it verifies the code is valid. Scientific mistake :-)) Read Karl Popper about it.