Некоторая информация об ANY-полях

bdr> Приветствую тебя Олег!
bdr> Ты писал:

>> Fld ANY
>> Fld &= WHAT(tst,1)
>> Если это действительно тебе или кому другому необходимо, то
>> могу бросить в эху письмо с кодом.
>> Oleg_Rudenko@mail.ru
>> Oleg_Rudenko@mailru.com

bdr> Извени, что с запозданием, но что то никто не захотел, а мне это
bdr> необходимо.
bdr> Если тебе не составит труда, то я бы не отказался от такой информации.
bdr> С уважением Бирюков Александр

ВНИМАНИЕ!!!
Все нижесказанное справедливо ТОЛЬКО для C55!
В версии C50 есть некоторые, незначительные, отличия.
Которые, впрочем, запросто могут привести к GPF!
Правда, надо заметить, что эти отличия касаются ТОЛЬКО правильного определения адреса UFO-обьекта, который в C55 определяетя просто через Address(Any).

В C50 необходимо немного «извратиться».
Если кому необходимо именно для С50 — пишите, подскажу.
Что-же касается описания INTERFACE и всех остальных структур, то они идентичны для обеих версий.

Как это работает в C56 — не знаю, нет ее у меня.

=============================================================
Кстати! Если у кого есть эта версия (C56) — может вышлите мне хотя-бы ее LIB-RTL, т.е. файл c56runxl.lib?
=============================================================

Итак.
Есть два способа «вытянуть» нужную инфу из ANY-поля:

1. Используя тот факт, что UFO-обьекты являются полноценными классами, можно воспользоваться их виртуальными методами,  доступ к которым можно получить через VMT.  Правда, через методы удастся получить не всю нужную инфу  о переменной, на которую ссылается данный UFO-обьект.  И, к сожалению, из-за странной организации большинства из методов  невозможно использовать INTERFACE, столь удобное для доступа  к классам из чужих библиотек. Приходится использовать или  переходничек на асм/с или писать для каждого метода свою  процедуру-переходник в самой Кларион-программе.  Поэтому этот способ я рассматривать не буду. А если кого  он и заинтересует, то буду рад помочь разобраться с ним.  По этому способу могу лишь порекомендовать некоторые  методы, которые по странной прихоти разработчиков были не только оформлены в отдельные дополнительные полноценные процедуры, но и даже экспортированы в DLL-библиотеку RTL:

MAP
UFO::Address(*? _Any),LONG,NAME('Cla$AddressUfo'),DLL(dll_mode)
! Возвращает адрес переменной по ее ANY-ссылке.

UFO::Size(*? _Any),LONG,NAME('Cla$SizeUfo'),DLL(dll_mode)
! Возвращает размер переменной по ее ANY-ссылке.
! Для массивов возвращает ОБЩИЙ размер ВСЕХ элементов массива

UFO::Select(*? _Any,LONG _Pos),*?,NAME('Cla$SelectUfo'),DLL(dll_mode)
! Позволяет получить доступ к отдельному элементу массива или строки
! по ANY-ссылке. Т.к. возвращает ANY-ссылку, то доступ к выбраному
! элементу осуществляется просто через нее:
! Str STRING(100)
! AnyStr ANY
! chAny ANY
! ...
! AnyStr &= Str
! ...
! chAny &= UFO::Select(AnyStr,5)
! if chAny = '0' then chAny = '1'.
! Аналогично работает и с массивами. В случае с многомерными
! массивами возвращает ANY-ссылку на соответствующий подмассив
! из первой размерности. Т.е.:
! Arr LONG,DIM(10,10) ! 10 строк по 10 колонок
! ArrAny ANY
! tAny ANY
! ...
! ArrAny &= Arr
! ...
! tAny &= UFO::Select(ArrAny,5)
! теперь в tAny имеем массив из 10 ячеек из 5 строки
! tAny = 100 - присвоить ВСЕМ ячейкам 5 строки значение 100
! tAny &= UFO::Select(tAny,3)
! теперь в tAny имеем ссылку на 3 ячейку 5 строки (Arr[5,3])
!

UFO::Slice(*? _Any,LONG _First,LONG _Last),*?,NAME('Cla$SelectUfo2'),DLL(dll_mode)

! Почти аналогична предыдущей процедуре. Работает ТОЛЬКО со строками,
! а для массивов (и прочих типов) выдает GPF.
! Возвращает ANY-ссылку на ПОДСТРОКУ из строки, на которую ссылается
! заданный UFO-обьект.
! В случае ЧТЕНИЯ значение подстроки аналогичен оператору
! Str = Sub(AnyStr,_First,_Last-_First+1)
! Уникален в случае, когда необходимо ПИСАТЬ что-то в подстроку,
! типа: Str[1:3] = 'Yes', т.к. напрямую, через ANY-ссылку
! такое не пройдет.
!
! chAny &= UFO::Slice(AnyStr,1,3)
! chAny = 'Yes'
! теперь в первых трех байтах строки, на которую ссылается AnyStr
! будет записано слово 'Yes'.
!
END

ВНИМАНИЕ!!!
Для данных процедур НЕПРИМЕНИМ аттрибут RAW!!!

И еще один ВАЖНЫЙ метод - определение типа переменной, на
которую ссылается ANY-переменная. Прямой функции для этого,
к сожалению, нет. Точнее, она есть, но ТОЛЬКО в LIB-RTL.
В DLL-RTL она (почему!?) не была экспортирована.
На всякий случай - UfoType(*? ANY),BYTE,NAME('Cla$UfoType')
Ну а мы будем использовать другой способ опредления типа,
который работает в обеих вариантах RTL - LIB и DLL.

Как я уже упомянул, большинство методов нельзя вызвать, используя
вариант с INTERFACE. Но с простыми методами, к которым относится
и метод определения типа переменной, можно работать и через INTERFACE:

TUFO_CallInterface INTERFACE,TYPE
Dummy1 PROCEDURE
Dummy2 PROCEDURE
Dummy3 PROCEDURE
Dummy4 PROCEDURE
Dummy5 PROCEDURE
_Type PROCEDURE(*? _Any),LONG !+14h
Dummy6 PROCEDURE
Dummy7 PROCEDURE
Dummy8 PROCEDURE
Dummy9 PROCEDURE
Dummy10 PROCEDURE
Dummy11 PROCEDURE
Dummy12 PROCEDURE
Dummy13 PROCEDURE
Dummy14 PROCEDURE
Dummy15 PROCEDURE
Dummy16 PROCEDURE
_Address PROCEDURE(*? _Any),LONG !+44h
Dummy17 PROCEDURE
Dummy18 PROCEDURE
Dummy19 PROCEDURE
Dummy20 PROCEDURE
_Max PROCEDURE(*? _Any),LONG !+58h
_Size PROCEDURE(*? _Any),LONG !+5Ch
END

Таким образом, тип переменной, например, можно определить так:

UFO &TUFO_CallInterface
Any ANY

...
UFO &= Address(Any)
Type# = UFO._Type(Any)

Костанты основых типов языка описаны в файле Equates.clw
Для массива метод _Type возвращает тип элемента массива.
Метод _Address аналогичен функции Cla$AddressUfo.
Метод _Size аналогичен функции Cla$SizeUfo.
Метод _Max возвращает общее кол-во элементов в массиве.
Для других типов он возвращает 0.

Правда, для ANY-ссылки на группу этот метод вернет кол-во полей  в группе. Но в нормальных условиях создать такую ANY-ссылку весьма  затруднительно. Если использовать терминологию антивирусов, то  «в свободной форме не встречается.»:)  Именно поэтому метод _Max можно использовать для определения  факта работы с ANY-ссылкой на массив. С помощью этого метода и  функции Cla$SelectUfo можно легко «раскрутить» массив любой  размерности до нужного элемента. А уже дальше можно с ним  легко работать. Чего, к сожалению, не умеют делать стандартные  операторы WHAT/WHO/WHERE, которые на попытку работы с полем-массивом, выдают неизменный GPF!

2. Второй способ предполагает получение необходимой  информации об UFO-обьекте по его свойствам.  Структура UFO-обьекта идентична как для C50 так и для C55.  Для C56, опять-же, не в курсе. Как обстоят дела в младших  версиях так-же не в курсе. Не интересовался, не было надобности.

Сразу-же определимся с типами UFO-обьектов. Их два:

Value UFO-обьекты

Содержат не ссылки на переменные, а сами значения этих переменных или просто константы. Образуются при присваивании типа: Any = 10. При этом создается Value UFO-обьект, содержащий константу LONG-типа со значением 10.
В случае присваивания строки создается также Value UFO-обьект, но он содержит уже адрес выделенной области памяти, в которую и записана эта строка.

Variable UFO-обьекты

Содержат именно ссылки на переменные.
Как их различать, если оба типа UFO-обьектов спокойно могут адресоваться одной ANY-переменной?
С помощью функций UFO::Address.
Для Value UFO-обьекта эта функция вернет 0 (ноль).
Ноль, так-же, естественно, возвращается и в случае, если ANY-переменная = NULL.

Т.е. алгоритм определения типа UFO-обьекта следующий:

NullUFO EQUATE(0)
ValueUFO EQUATE(1)
VarUFO EQUATE(2)
...
if Any &= NULL then UFOType# = NullUFO else
if UFO::Address(Any) = 0
UFOType# = ValueUFO
else
UFOType# = VarUFO
. .

Структура ValueUFO-обьекта:

TVarUFO_Header GROUP,TYPE
VMTPtr LONG !+00h VMT address
Value REAL !+04h Именно само значение
StrPtr LONG,OVER(Value) !+04h Адрес области памяти, где
! записана строка
Type BYTE !+0Ch Тип значения
END

Структура VarUFO-обьекта:

TVarUFO_Header GROUP,TYPE
VMTPtr LONG !+00h VMT address
DataPtr LONG !+04h Адрес блока данных
Flags BYTE !+08h Флажки (1бит=1 - надо освободить DataPtr)
DataSize UNSIGNED !+09h Размер строки или группы
Decimal GROUP,OVER(DataSize) ! Для DECIMAL и PDECIMAL
Size BYTE !+09h Размер в байтах
Float BYTE !+10h Кол-во цифр в дробной части
END
ArrayBaseUFO LONG,OVER(DataSize) !+09h UFO элемента массива
ArrayDim LONG !+0Dh Кол-во элементов в массиве
END

Ну, а так как мы уже умеем определять тип VarUFO-обьекта (см.1), то запросто сможем «вытащить» необходимую нам информацию.

Если кто помнит, то изначально вопрос стоял так — как можно определить параметры DECIMAL-поля в группе.

Используя все вышесказанное мы сейчас это и проделаем:

Grp GROUP
Fld1 LONG
Fld2 DECIMAL(13,2)
Fld3 STRING(10)
END

aFld ANY
UFO &TUFO_CallInterface
VarUFO &TVarUFO_Header

! Предполагаем, что задан или номер поля в группе или его имя.
! Как по этим параметрам «вытащить» ANY-ссылку на поле из группы,
! уже много говорилось в этой эхе. Поэтому углубляться не будем.
! Предположим — есть номер поля, FldNum.

...
aFld &= WHAT(Grp,FldNum)
if ~(aFld &= Null)
UFO &= Address(aFld)
Type# = UFO._Type(aFld)
if (Type# = DataType:DECIMAL) OR (Type# = DataType:PDECIMAL)
Loop While UFO._Max(aFld) ! "раскручиваем" массив, если надо
aFld &= UFO::Select(aFld,1)
UFO &= Address(aFld)
.
VarUFO &= Address(aFld)

! ВНИМАНИЕ!!! Вообще-то, как я уже неоднократно писал
! в данной эхе, таким образом мы НЕ СОЗДАЕМ ПОЛНОЦЕННЫЙ
! реферал на группу. НО, в данном случае, так как
! типовая структура TVarUFO_Header описана в области
! «видимости» компилятора и мы НЕ собираемся использовать
! этот реферал для передачи его в другие процедуры и
! НЕ собираемся делать «глубокое присваивание», то
! такой неполноценный реферал вполне нам подойдет.

Digits# = (VarUFO.Decimal.Size*2)-1
Float# = VarUFO.Decimal.Float
Message(WHO(Grp,FldNum) &' DECIMAL('& Digits# &','& Float# &')')
. .

Одно лишь замечание. Как известно из доки по Клариону, переменные  DECIMAL-типа ЛЮБОГО размера ВСЕГДА содержат НЕЧЕТНОЕ кол-во цифр.  Например, DECIMAL(12,2) и DECIMAL(13,2) занимают 7 байт и в них  помещается 13 цифр. Именно поэтому декларации DECIMAL-полей будут  определятся ВСЕГДА с нечетным кол-вом цифр.

Ну вот, вкратце, и все!