Перебираю поля группы циклом. Как мне отличить корректные значения AnyVar от некорректных?

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# — является ли полученная нами переменная массивом.

А вот как легально «вытащить» из такого массива нужный элемент — не знаю. Если только, через адрес переменной + смещение_нужного_элемента.
В общем, здесь — полно извратов!