Еще один новый недокументированный тип

Если кто помнит, в предыдущем письме а писал, что в Кларе «обнаружился» новый тип переменной размером в 16 байт. Так вот, компилятору этот тип известен под именем VARIANT. Не знаю, есть ли он в предыдущих версиях, но в С5 уже есть.

NewVar  VARIANT

Он представляет собой, если можно так сказать, симбиоз двух типов — ANY и BSTRING. Позволяет присваивать любые значения. При этом строки хранит, как и BSTRING, в юникоде. При присваивании чисел не преобразует их в строку, как BSTRING, а хранит, как и ANY, в числовом виде. Надо иметь ввиду, что переменные типа ULONG предварительно преобразуются в REAL. Также преобразуются в REAL и DECIMAL-переменные. Заметьте, что при этом будут утеряны дробные разряды старше 15-го. При обратном присваивании числовым переменным значение из VARIANT всегда преобразуется в REAL. В этом плане поведение аналогично ANY-переменным.

Но, в отличии от ANY, не позволяет присваивать рефералы на переменные. Хотя компилятор генерит необходимый для этого код с привлечением функций ядра по работе с типом VARIANT. Так что, очевидно, существуют особые ситуации, когда такое присваивание возможно. Или в данной версии еще не полностью проработан данный тип.

Надо заметить, что в некоторых случаях для работы с переменными этого типа используются функции из OLE-библиотеки. Так что, возможно, этот тип и предназначен, в основном, для работы с OLE-обьектами.

Вот, в принципе, и все о новом типе.

Кстати!
У файла также «обнаружился» новый атрибут — CDROM. Драйвер его опознает, но что он означает — обнаружить не удалось. Но точно не READONLY, как можно было бы предположить!

Multylanguage base

VS> А вот что с Clarion’oм делать? Ждать документирования BString? Или попробовать ее заюзать?

А чего ждать? Я, кажется, недавно описывал суть, возможности и способы использования строк этого типа. На всякий случай, напомню:

  • строки данного типа представляют собой хранилища данных в юникоде динамического размера.
  • при присваивании значения такой строке, сначала освобождается ранее занятая ею память (если уже была инициализация), после чего выделяется новый блок памяти размером, как минимум, равным удвоенной длине нового значения + 4(2 для 16-бит) байта для длины.
  • при записи нового значения в выделенную память происходит конвертация обычного ANSI-текста в юникод.
  • при считывании значения такой строки автоматом происходит обратная конвертация юникода в ANSI-текст.
  • до первого присваивания поле такого типа содержит нулевой указатель, что, впрочем, не приведет к ошибке при операции типа: Str = BStr, т.к. функции ядра по манипулированию данными в стеке корректно обрабатывают нулевые указатели.

Т.е., само по себе такое поле представляет Читать далее

Буфер записи (top secret) #2

VY> PS.
VY> А вот еще интересный момент, как например сработать «указатель» на буфер записи по аналогии с C55. То есть, чтоб некоторая процедура позволяла получить его как &GROUP (без магического обрамления в группу).
VY> C55:
VY> RecPtr &GROUP
VY> ….
VY> RecPtr &= MyFile{PROP:Record}
VY> C5:
VY> RecPtr &GROUP
VY> ….
VY> А здесь процедура должна этот RecPtr «указать».
VY> У меня получилость только так (с обрамлением):
VY> !********************
VY> FRecGrp       GROUP
VY> FRecord         &GROUP
VY>              END
VY>  CODE
VY>  GetFileRecord(TestFile, FRecGrp)
VY> ! Теперь FRecGrp.FRecord — то что нам надо.
VY> !********************
VY> GetFileRecord PROCEDURE(FILE MyFile, *GROUP RecGrp)

Иначе и не получиться. Я уже писал про то, что компилятор не позволяет вернуть из функции указатель на группу. Даже в С55. Новое свойство {PROP:Record} просто исключение, которое по особому обрабатывается компилятором.

У меня, вообще, есть подозрение, что компилятор знает как обрабатывать каждое свойство! Т.е., вместо того, чтобы сделать универсальный механизм свойств объекта, которые обрабатываются исключительно внутренними методами объекта без вмешательства компилятора, разработчики опять «намудрили»! Сами свойства, как и положено, обрабатываются внутренним механизмом объекта: у файла/View — драйвером файла, у контролов окна — методами класса контрола. Но вот насчет возвращаемого значения — тут опять работает компилятор. Если, например, написать дополнительный драйвер объекта, который будет обрабатывать дополнительные свойства, то в большинстве случаев ничего путного из этого не получиться. Компилятор просто не будет знать, что делать с возвращаемым результатом. Так что, увы, пока у разработчиков не получился универсальный механизм работы со свойствами объектов.

Кстати, Владимир. Ты спрашивал насчет моих писем. Обычно все письма насчет внутренней работы Кларион шли с заголовками типа: «Внутренняя реализация Клариона» и «Некоторые особенности при работе с …» Вместо точек — GROUP/QUEUE/FILE/VIEW/CLASS.

Обычно я их слал в оба списка рассылки. К сожалению, в отличие от списка clarion@yahoogroups.com, у данного списка нет архива писем. По крайней мере, на сайте АРСИСа я не видел никакого упоминания на этот счет. А жаль! Было бы неплохо, если бы такой архив был в данном списке.

P.S. В предыдущем письме я спрашивал — не знает ли кто как можно передавать в функцию указатели на НЕИМЕНОВАННЫЕ классы и возвращать из функций указатели на них. Так вот, если кому интересно — мне удалось это сделать. Правда, возврат получается немного некрасиво, типа:

MyClass &= Address(GetClassRef())

Зато — работает. И не надо предпринимать какие-либо специальные меры, типа помещения данного указателя в группу.

Буфер записи (top secret)

VY> С5ee-b
VY> Неоднократно уже поднимался вопрос,
VY> как зная только метку файла получить доступ к буферу записи.
VY> Неоднократно давались
VY> ответы, примерно так:
TestProc PROCEDURE(*FILE MyFile)
FileHeader GROUP
DriverPtr ULONG !указатель на точку входа драйвера файла
NamePtr &STRING !указатель на строку именифайла (так можно только в 32-бит)
DrvInitPtr &STRING !указатель на строку инициализации драйвера (так можно только в 32-бит)
PasswordPtr &STRING ! указатель на строку пароляфайла (так можно только в 32-бит)
Status BYTE ! атрибуты файла
KeyCount BYTE !количество ключей
MemoCount BYTE !количество MEMO-полей
RecordSize ULONG !размер записи (буфера записи)
FieldsDefPt ULONG !адрес структуры описания полей записи файла 
KeysDefPtr ULONG ! адрес структуры описания ключей файла
MemosDefPtr ULONG ! адрес структуры описания MEMO-полей
RecordPtr &GROUP !указатель на буфер записи 
         END

???? Это где-же ты такое нашел?
RecordPtr      ULONG
Только ТАК и никак иначе!!! А как составить из этого правильный указатель на запись см. в конце письма.

А в 16 бит не только указатели на строки меняются на простые LONG, но и все размеры задаются через SHORT. Читать далее

Динамические очереди

bdr>  Меня интересуют твои наработки по динамическим очередям. Как я понимаю это реализовано у тебя в виде библиотеки(lib или dll).  Возможно ли получить от тебя dll, если да, то не кинеш ли на мыло baa@dionis.ru.  Заранее благодарен!

Я планирую на праздники (первые) отделить от данной библиотеки класс DynaFile по причине его «сырости» и тогда брошу все необходимое, например, на Клариошу. Иначе мой инет просто физически не потянет рассылать отдельно всем заинтересованным. Как и DynaView, это будут LIB и DLL модули для 32бит. Читать далее

Некоторые особенности при работе с QUEUE #2

Помнится, не так давно в наших эхах шли обсуждения на тему эмуляции Set для очередей. В особенности, когда очередь отсортирована, например, по двум полям, и надо встать на начало или конец какой-либо последовательности по первому полю.

Как известно, в C55 появился новый оператор, позволяющий это делать — POSITION().
Но! Есть подобный оператор, точнее функция, и в C5!

  Map
    Module('')
      RTL::Position(QUEUE _Que,LONG _F1=0,...),LONG,RAW,C,NAME('Cla$POSITIONqueuekey')
    .
  .

Использование данной функции несколько отличается от POSITION из C55:

  • POSITION(C55) работает только по текущей сортировке
  • POSITION(C5) работает по любой сортировке, т.к. принудительно, если необходимо, делает SORT.

В остальном логика работы и возвращаемый результат одинаковы для обеих функций.
Для POSITION(C55) не проверял, но POSITION(C5), кроме того, выставляет POINTER на (POSITION(C5)-1). Кроме случая, когда возвращается 0. Тогда POINTER так-же равен 0.

А теперь — о плохом 🙁
К сожалению, разработчики не экспортировали данную функцию в DLL-библиотеку! Таким образом, использование данной функции возможно ТОЛЬКО для LOCALE-варианта сборки приложения.
Вот такой вот облом! Главное не понятно — ПОЧЕМУ !!!

Некоторые особенности при работе с QUEUE

Все прекрасно знают, как работать с очередями используя ключевые поля.
Типа:

Add(Queue,+QUE:Field1,-QUE:Field2)
Put(Queue,+QUE:Field1,-QUE:Field2)
Get(Queue,+QUE:Field1,-QUE:Field2)
Sort(Queue,+QUE:Field1,-QUE:Field2)

Есть еще форма использования данных операторов с ключевой строкой, где задаются внешние имена полей (аттрибут NAME()). Но к данной теме эта форма не имеет отношения.

Но! Довольно часто было бы неплохо задавать в качестве ключевых полей их номера в структуре записи очереди. Стандартные операторы такого не позволяют, но можно использовать для этого их внутренние представления, которые как раз и работают с номерами полей.

MAP
  MODULE('ClaRTL')
    QUE::Add(QUEUE _Que,LONG _F1=0,LONG _F2=0,LONG _F3=0,LONG _F4=0,LONG |
    _F5=0,LONG _F6=0,LONG _F7=0,LONG _F8=0,LONG _F9=0,LONG _F10=0,LONG=0) |
    ,RAW,C,NAME('Cla$ADDqueuekey')
    QUE::Put(QUEUE _Que,LONG _F1=0,LONG _F2=0,LONG _F3=0,LONG _F4=0,LONG |
    _F5=0,LONG _F6=0,LONG _F7=0,LONG _F8=0,LONG _F9=0,LONG _F10=0,LONG=0), |
    RAW,C,NAME('Cla$PUTqueuekey')
    QUE::Get(QUEUE _Que,LONG _F1=0,LONG _F2=0,LONG _F3=0,LONG _F4=0,LONG |
    _F5=0,LONG _F6=0,LONG _F7=0,LONG _F8=0,LONG _F9=0,LONG _F10=0,LONG=0), |
    RAW,C,NAME('Cla$GETqueuekey')
    QUE::Sort(QUEUE _Que,LONG _F1=0,LONG _F2=0,LONG _F3=0,LONG _F4=0,LONG |
    _F5=0,LONG _F6=0,LONG _F7=0,LONG _F8=0,LONG _F9=0,LONG _F10=0,LONG=0), |
    RAW,C,NAME('Cla$SORTqueuekey')
    QUE::Kill(QUEUE _Que),RAW,NAME('Cla$FREEqueue')
  END
END

В принципе, можно задать и больше или меньше параметров в декларации этих процедур. Здесь надо исходить из того, что все (и пустые) параметры перед вызовом процедуры «забрасываются» в стек. Поэтому, если задать слишком много параметров, вызов может занять некоторое время. В любом случае декларировать эти процедуры следует так, чтобы при любом вызове всегда оставался последний «нулевой» параметр, т.к. «нулевой» параметр является для данных процедур признаком завершения списка активных параметров.

QUE::Sort(Queue,1,-2) - Sort(Queue,+QUE:Field1,-QUE:Field2)

Последняя процедура QUE::Kill() может быть полезна при использовании большого кол-ва очередей. Как известно, при первом же обращении к очереди, для данной очереди создается блок служебных параметров размером ~1Kb. Данный блок освобождается ТОЛЬКО при завершении программы или при уничтожении очереди, если она была создана динамически через New(). Не существует других легальных способов освободить память, выделенную под этот служебный блок. Так вот данная процедура (QUE::Kill) позволяет как раз сделать это совершенно легально. Надо, также, иметь ввиду, что после применения данной процедуры ничего не мешает использовать эту очередь в дальнейшем. При следующем же обращении к этой очереди служебный блок будет создан заново. Другими словами, процедура QUE::Kill позволяет как бы эмулировать процесс создания-удаления динамических очередей операторами New()-Dispose().

Некоторые особенности при работе со структурами типа GROUP

Думаю, многие знают, что такое Deep Assignment, или по нашему «глубокое присваивание»? Для тех, кто не в курсе — почитайте про это в Help`e.

Так вот, будет полезно знать некоторые особенности данной фичи, в применении к группам/классам/очередям/файлам.

1. Внутренняя реализация данной фичи основана на так называемом «списке соответствия полей», где каждому полю одной структуры сопоставлено поле с таким же названием из другой структуры. Используются, естественно, просто номера полей в своих структурах. Данный список составляется КОМПИЛЯТОРОМ во время компиляции программы. Таким образом данная фича НЕ РАБОТАЕТ для не типизированных реферал-указателей! Грубо говоря, разработчики просто поленились, так как данный список прекрасно строится на основе той инфы о группах, которая доступна Читать далее

Key — File

ЧС> Как получить указатель на ключ имея указатель на файл — известно. Как получить указатель на файл имея указатель на VIEW — известно. А как получить указатель на файл имея указатель на ключ?

У тебя же, на Клариоше, лежит моя либа DynaView, где среди других сервисных функций есть и такая! Причем все сервисные функции в этой либо не требуют создания самого View. Достаточно просто объявить экземпляр класса DynaViewClassType в секции глобальных или локальных данных.
Ну, а если влом, то — держи:

  MAP
    KeyOwnerFile(KEY CheckKey),*FILE
  END

KeyOwnerFile PROCEDURE(KEY CheckKey)

KHdr                 GROUP
Name                   &STRING
Label                  &STRING
Pipe                   ULONG
Attr                   UNSIGNED
FieldCount             BYTE
Fields                 ULONG
File                   &FILE
                     END
KGrp                 GROUP
KRef                   &KEY
                     END
KPtr                 ULONG,OVER(KGrp)

  Code
  if CheckKey &= Null then Return Null.
  KGrp.KRef &= CheckKey
  Peek(KPtr,KHdr)
  Return KHdr.File

Некоторые особенности при работе с BSTRING и ASTRING #3

YF> А поподробнее про этот тип данных можно?  В частности, можно ли писать Use(Bstring)? Например, Use(Astring) в CW5 не работало, а в CW55 вроде работает….

Да про нее и писать-то особенно нечего!

Данный тип автоматически преобразует записанные в него данные согласно универсальной символьной таблице, когда на каждый символ выделяется два байта.

Т.е.,если делаем присвоение, например, BStr = ‘12345’, то в памяти эта строка будет храниться как ‘1<0>2<0>3<0>4<0>5<0>’. Национальные символы будут преобразованы в соответствующие им двухбайтовые последовательности. Для программы это все делается прозрачно. Этот тип позволяет нормально работать с API-функциями, которые в качестве строковых параметров требуют строки такого формата. Раньше для этого приходилось формировать такие строки вручную.

В отличии от ASTRING, выделенная память для BSTRING никак Кларионом не учитывается. Это обстоятельство, кстати, уменьшает накладные расходы памяти для BSTRING до 4 байт, которые стоят непосредственно перед строкой и содержат длину данной строки (с учетом 2байта/символ).

А что касается USE, я так и не понял, что у тебя не работало в C5? У меня в тестовом примере на C5eeb нормально работает окно с двумя строковыми полями для ввода — ASTRING и BSTRING.