Архив рубрики: Статьи Руденко Олега

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

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.

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

AN>     Я, может и щас глупость скажу, но ИМХО сей ASTRING есть ни что иное, как виндовый атом и такое поведение для него вполне естественно, т.е. при создании строки RTL лезет в таблицу атомов, ежели там такой нет, то добавляет, если есть, то увеличивает счетчик обращений к этой строке и возвращает уже ея.  При удалении строка физически удаляется из таблицы только ежели счетчик равен 0. (ну прям как FileManager в ABC с откр/закр фалов :)).

Так, ASTRING именно так и заявлена разработчиками. Как аналог атома. Я с виндовыми атомами не разбирался, но, исходя из твоего описания, могу сказать, что ASTRING несколько отличается от атома:

  • у ASTRING нет счетчика
  • она физически удаляется ТОЛЬКО при завершении программы.

Кстати, на каждую ASTRING уходит, как минимум, от 14 до 22 байт доп. памяти.

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

Думаю всем будет полезно узнать, что при использовании типа BSTRING в группах/очередях/классах надо запомнить следующее правило:

  • если группа/очередь/класс объявлены в глобальных данных, то в конце программы надо ОБЯЗАТЕЛЬНО ставить Clear(GROUP/CLASS). В случае использования очереди надо ОБЯЗАТЕЛЬНО пройтись по каждой записи и или очистить ее всю или сделать Clear() полям BSTRING.
  • если группа/очередь/класс объявлены как локальные для процедуры, то в конце процедуры надо выполнить аналогичные действия как и для глобальной.

В случае использования BSTRING в очередях Читать далее

DynaView

Заинтересовалось несколько человек. Спасибо! Брошу пример и библиотеку чуть позже. Просто добавил еще пару возможностей. В частности — возможность создания динамической группы на основе уже созданной другой. Что-то типа виртуального LIKE. Так что, как протестирую — сразу брошу.

Хотя, вот с примером — проблема в том, какой сделать пример? Моя тестовая программка для этого не годится — слишком много в ней лишней служебной информации. Так что — принимаются любые предложения. Читать далее

Некоторые особенности при работе с CLASS`ами

Я думаю многие обрадуются, если я сообщу, что есть возможность работать с любыми переменными любых классов!
Независимо от атрибута Private или Protected!

Особенно это актуально при работе с ABC-шаблонами, чьи классы организованы не самым лучшим образом! Теперь, для доступа к Private-переменным, нет необходимости в модификации исходников.

Все очень просто решается использованием функций What/Who/Where! Читать далее