YF> Перебираю поля группы циклом.Для каждого поля
YF> AnyVar &= What(Group,FiedlNo)
YF> Как мне отличить корректные значения AnyVar от некорректных?
YF> Дело в том, что если поле имеет совсем неподходящий тип
YF> (массив или ссылка), присваивание всё равно срабатывает,
YF> но Any-переменная ни к чему не пригодна.
YF> Вопрос, скорее всего, к Олегу Руденко….
Когда поле описано как ссылка, то What() и вернет значение этой ссылки. Значение ИМЕННО самой ссылки, а не переменной, на которую указывает эта ссылка! Дело в том, что все ссылки в группе описаны одним общим типом REFERENCE(1Fh). Отличаются они только размером, который также передается в ANY. Если известен тип ссылки (тогда зачем What()?:)), то довольно легко получить и значение переменной, на которую ссылается это поле:
TGrp GROUP Desc &STRING END GRef GROUP TmpS &STRING END Var ANY Code TGrp.Desc &= New(STRING(100)) TGrp.Desc = '1234567890' Var &= What(TGrp,1) ! получили значение ссылки Desc GRef = Var ! TmpS &= TGrp.Desc Message(GRef.TmpS) ! Выведет '1234567890' Dispose(TGrp.Desc) ! А можно и - Dispose(GRef.TmpS) ! Var = 0
Если тип ссылки неизвестен, то можно его определить по размеру. Правда, в таком случае точно определить можно только ссылки:
- &STRING/&CSTRING/&PSTRING (size = 8byte) - &GROUP (size = 12byte) - &DECIMAL/&PDECIMAL/&BLOB (size = 6byte)
Все остальные ссылки имеют размер 4 байта.
Определение размера поля, полученного через Var &= What():
TVarUfo GROUP VMT LONG ! Адрес VMT VarUfoClass DataPtr LONG ! Адрес переменной Flag BYTE ! х.з. InfoGrp GROUP ! информация по типам DataSize UNSIGNED ! размер переменной Rezerve LONG END Decimal GROUP,OVER(InfoGrp) ! для DECIMAL/PDECIMAL Size BYTE ! размер Float BYTE ! кол-во цифр в дробной части END Array GROUP,OVER(InfoGrp) ! для массивов Element ANY ! информация об элементе массива Dims USHORT ! кол-во элементов в данном измерении END END
DataSize заполняется только для типов:
- STRING/CSTRING/PSTRING
- GROUP (преобразуется в STRING)
- REFERNCE (все указатели)
Для всех остальных простых типов размер определяется по типу переменной.
Для массивов: если имеем многомерный массив, то сначала идет описание первого измерения, в Array.Element получаем указатель на описание следующего измерения, и только в последнем измерении получаем ссылку уже на сам элемент массива.
Peek(Address(Var),UfoAddr#) if UfoAddr# ! что-бы избежать GPF при нулевом адресе Peek(UfoAddr#,TVarUfo) ! TVarUfo.DataSize - размер .
Сам тип переменной, полученной по ANY, определить не так просто. В ядре есть подобная функция, но она задекларирована только в LOCALE-режиме компиляции. А чтобы всегда можно было определить тип переменной, придется вызвать виртуальный метод VarUFO-класса через адрес таблицы виртуальных методов TVarUfo. VMT. Нужный нам метод находится там под шестым номером или по смещению 20(14h):
MAP UfoTypeProc,LONG,TYPE AnyType(UfoTypeProc),LONG,NAME('GetAnyType') MODULE('') GetAnyType(ProcAddr),LONG,NAME('GetAnyType') END END Code if ~TVarUFO.VMT then Type# = 0 else Type# = GetAnyType(TVarUFO.VMT+20) . AnyType PROCEDURE(UfoTypeProc) Code Return(UfoTypeProc())
Для массивов, к сожалению, таким образом можно получить только тип элемента самого массива, но невозможно определить, что полученная нами переменная является массивов. Можно только анализировать данные из группы TVarUfo и на их основе принимать решение. Правда, есть один «хитрый» способ. Но он проходит на уровне «as-is», т.е. нет никакой гарантии, что в он будет работать всегда и везде. Основан он на том, что для группы TVarUfo для массивов выделается несколько больший блок памяти, чем для обычных переменных:
Type# = 0; Array# = 0 if UfoAddr# Peek(UfoAddr#,TVarUfo) if TVarUFO.VMT Type# = GetAnyType(TVarUFO.VMT+20) Peek(UfoAddr#-8,MBSize#) if MBSize# = 20h then Array# = True. . .
Если все нормально, то в Type# имеем тип переменной или тип элемента массива. В Array# — является ли полученная нами переменная массивом.
А вот как легально «вытащить» из такого массива нужный элемент — не знаю. Если только, через адрес переменной + смещение_нужного_элемента.
В общем, здесь — полно извратов!