AA> А как бы почитать сии дебаты, учитывая, что я позжее подписался на
AA> КлаЛист? 🙂
ВНИМАНИЕ!!!
Все нижесказанное справедливо ТОЛЬКО для C55!
В версии C50 есть некоторые, незначительные, отличия.
Которые, впрочем, запросто могут привести к GPF!
Правда, надо заметить, что эти отличия касаются ТОЛЬКО правильного определения адреса UFO-обьекта, который в C55 определяетя просто через Address(Any).
В C50 необходимо немного «извратиться».
Если кому необходимо именно для С50 — пишите, подскажу.
Что-же касается описания INTERFACE и всех остальных структур, то они идентичны для обеих версий.
Как это работает в C56 — не знаю, нет ее у меня.
Итак.
Есть два способа «вытянуть» нужную инфу из 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-обьектов. Их два:
a. Value UFO-обьекты
Содержат не ссылки на переменные, а сами значения этих переменных или просто константы. Образуются при присваивании типа: Any = 10. При этом создается Value UFO-обьект, содержащий константу LONG-типа со значением 10. В случае присваивания строки создается также Value UFO-обьект, но он содержит уже адрес выделенной области памяти, в которую и записана эта строка.
b. 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-полей будут определятся ВСЕГДА с нечетным кол-вом цифр.
