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-полей будут определятся ВСЕГДА с нечетным кол-вом цифр.