модификация QUEUE и пр..

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