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

SET() и всякие глупости

>> >> >> А че будет, ежли и правда ключ чему-то приравнять? :-)))
>> >> AM> не понял, ключ приравнять то конЭчно нельзя а вот группу можно
>> >> AM> получилось то что ты и хотел, те групповое присвоение полей в СО> ключе, это
>> >> AM> имелось ввиду ?
>> >> Дык группы присваивать я уже умею ;). А то было б непосредственно ключ
>> >> 🙂
>> VVS> Вот более непонятно отсутствие ошибки при использовании
>> VVS> ключей не от тех файлов в файловых операциях.
>> VVS> Типа SET(File1:Key,File2:Key) и т.п.

СО> есть подозрение что File1:Key,File2:Key — это просто порядковый номер ключа
СО> SET(1,1) — Может работать…. Грубо и наверно не так.
СО> Хотя есть подозрение что в результате вынимается номер ключа….

Немножко не так.

На самом деле все достаточно просто: в данной форме оператора SET() второй параметр используется ТОЛЬКО для определения самой операции — «выставить указатель в ключе по полям данного ключа». А само значение этого второго параметра драйвером не используется. Поэтому в качестве второго параметра может стоять ЛЮБОЙ ключ. Все данные о рабочем файле и ключевым полям берутся из ключа, заданного в качестве первого параметра.

Другое дело, что выглядит это малость странновато и должно бы пресекаться на этапе компиляции. Хотя думаю, что знаю, почему компилятор не делает подобной проверки — в качестве параметров оператора SET() могут быть использованы рефералы на ключи. А их принадлежность какому-либо файлу можно определить ТОЛЬКО во время выполнения.

Кстати! В рамках данной темы поднимался вопрос о сравнении скорости работы цикла чтения записей прямо из файла или посредством View. Как показывает опыт (и не только мой), чтение через View практически НИКОГДА не бывает медленнее прямого чтения из файла. Но, во многих случаях, чтение через View быстрее прямого чтения из файла. Иногда — на порядок!

Основные преимущества по скорости у View в следующих случаях:

  • использование фильтрации записей. Особенно, когда фильтр не имеет прямой поддержки со стороны ключей.
  • использование порядка сортировки, отличного от имеющихся ключей.
  • работа с SQL-таблицами.

Небольшое дополнение к первому пункту: при использовании фильтра, который не «ложиться» ни на один из имеющихся ключей, разница в скорости выборки будет тем больше, чем меньше кол-во записей, удовлетворяющих всем условиям выборки.

SET()

PS> Здравствуйте, люди!
PS> Хочу выбрать из большого файла записи со значением полей: code_doc=8 и
PS> gBegDate<=data_last и data_last<=gEndDate. Чтобы не просматривать весь файл,
PS> я решил создать ключ из code_doc и data_last, потом установить режим прохода
PS> по файлу по этому ключу и прыгнуть на первую запись, у которой нужный
PS> code_doc (8). Как это сделать? Насколько я понял, с помощью оператора SET().

PS>   Строчка из описания файла:
PS> code_date_key KEY(MOV:code_doc,MOV:data_last),DUP,NOCASE,OPT

PS>   Кусочек программы:
PS>   OPEN(moving)
PS>   MOV:code_date_key=8
PS>   SET(MOV:code_date_key, < ? > )
PS>   LOOP UNTIL EOF(moving) OR (MOV:code_date_key<>8)
PS>      NEXT(moving)
PS>      ….
PS>   END
PS>   Что здесь неправильно и что должно стоять в качестве второго параметра
PS> SET? Надо ли присваивать что-то MOV:data_last ? Ну тогда уж минимальное
PS> возможное значение, т.к. меня интересуют все даты с этим code_doc. А какое
PS> оно?

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

Свои библиотеки (в исходном тексте)

PS>   С неиспользуемыми кусками кода все понятно — это свойство компилятора, а
PS> не линковщика. С lib-модулями — тоже. Например, в Borland C++ lib-файлы
PS> (библиотеки) собираются из obj-файлов с помощью специальной программы.
PS>   А вот с obj-файлами — непонятно. Копирую их другого своего письма:
PS>   Может ли obj-файл, созданный компилятором при
PS> компилировании одной программы, использоваться в другой? Если не может —
PS> тогда понятно (он — «собственность» этого проекта). Если может — что
PS> наиболее вероятно (компилятор пишет что-то вроде «Создан qwe.obj (qwe.clw
PS> был изменен)») — то что, собственно говоря, помещать в этот obj-файл —
PS> процедуры, используемые в одном проекте, или процедуры, используемые в
PS> другом проекте? А если я создам один exe-файл, потом — другой, а потом
PS> вернусь к первому, что делать компилятору с obj-файлами? Как он узнает, что
PS> они были созданы для другого проекта?
PS>   А откуда Вы знаете про SmartLink? Наверное, там это объясняется.

Честно говоря, я этим делом специально не занимался. Но по опыту использования SOURСE — библиотек типа тех, что мы обсуждали по данной теме, могу сказать, что проблем никогда не было. Т.е., когда я собираю приложение, никогда не задумываюсь, а как у меня там с obj-модулями общих библиотек?

Стало интересно — посмотрел. Оказывается — все несколько не так Читать далее

Управление линковкой

NT> Можно собрать ToDo-заглушки для библиотек B или условно omit-ить вызовы.

Заглушки не подходят — некрасиво:) OMIT-ить вызовы не получится, так-как библиотека уже собрана и откомпилирована а условия конечной сборки определяются в самом приложении.

Вообщем, я уже решил эту проблему — в самом INC-файле основной библиотеки, используя COMPILE(), объявляю адресные метки всех «плавающих» переменных/процедур из подчиненных библиотек. А в главной библиотеке есть только список их адресов. При сборке библиотеки он пустой. Теперь, при сборке приложения, надо только с помощью Define-меток указать, какие переменные/процедуры будут использоваться в данном приложении и с помощью процедуры регистрации из главной библиотеки заполнить список адресов. Все это делает шаблон. Таким образом, получили эмуляцию динамического определения нужных адресов в статическом режиме! И линкер не ругается, и все работает.

В принципе, все это необходимо для новой библиотеки DynaFile. В этой библиотеке мне необходимо знать точки входа в файловые драйверы. В DLL-режиме их определить элементарно. А в статике их можно определить только если нужные LIB-библиотеки уже подключены к проекту. А так как эти точки входа определяются по символьным меткам драйверов, то эти самые символьные метки надо объявить в библиотеке. А если метка объявлена, но драйвер не подключен, то получаем отлуп от линкера. Вот и получается, что все драйверы подключать к проекту — нонсенс, а знать их адреса в библиотеке надо! И никакие условия компиляции уже не помогут, так как библиотека уже собрана.

>> А вот в данном случае — в библиотеке (А) обьявлено несколько
>> переменных или процедур, которые находятся в нескольких разных
>> библиотеках. При определенных условиях, в приложении некоторые
>> из этих переменных или процедур будут не нужны. Не хотелось-бы и
>> «цеплять» лишние библиотеки, которые не будут использованы.
>> Можно, конечно, для разных таких случаев собрать разные
>> варианты библиотеки (А). Но таких вариантом может быть масса.
>> Да и не красиво это как-то!

Address of FILE-driver

MAP
  ClarionDrv,NAME('CLARION')
  TopSpeedDrv,NAME('TOPSPEED')
END

Code
ClaDrvAddr# = Address(ClarionDrv)
TpsDrvAddr# = Address(TopSpeedDrv)

Compile project in Local mode. If drivers CLARION and TOPSPEED linked to project — all Ok.

If driver CLARION or TOPSPEED not linked to project — failed proccess of link. Ok. It is possible to make so, that the process of assembly of the project has not discontinued, and instead of addresses to receive 0?

Классы #3

bdr>  Прочитал твои два послания. Спасибо за информацию.
bdr>  У меня к тебе два вопроса, если есть время.
bdr>  1. Чем все таки лучше и правильно ли использовать
bdr>  Init       PROCEDURE(*GROUP _objB)
bdr>   или как приводил Андрей передавать адрес класса в STRING
bdr>   и почему неиспользовать
bdr>   Init       PROCEDURE(ClassB _objB)

Как внес поправку Андрей — передавать адрес через строку удобнее при запуске нового потока. Так как только строки возможно передавать в качестве параметров. При обычном вызове — лучше передавать через группу, т.к. — это «родной» формат для класса. Использовать в качестве параметра класс — тоже правильно. Другое дело, что при таком описании ты не сможешь в эту процедуру передать другой класс. А при передаче через строку или группу можно передавать любые классы.

bdr>  2.Второй вопрос несколько на другую тему, но о классах
bdr>  Описаны 3 типа класса: класс А; и В и С порожденные от А
bdr>  В некую процедуру(это может быть и метод вообще другого класса)
bdr>  в качестве параметра передаются класс типа В или С
bdr>   ( Procedure(ClassA CurrentClass)). Как в этой процедуре определить
bdr>  что передан класс типа В или класс типа С, неспользуя дополнительных
bdr> параметров.  В DELPHY для этого есть AS.

Два способа:

я иногда завожу в классах строковую переменную, в которую при инициализации записываю название типа класса.

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

Есть, правда, еще один способ. Посложнее. Класс передается в процедуру по адресу его буфера. Первым лонгом в этом буфере идет адрес таблицы виртуальных методов. Вот уже он — разный для разных классов. Он назначается на этапе компиляции. Так что, в принципе, в начале программы можно составить некую таблицу этих адресов VMT для разных классов и уже по ней идентифицировать классы. Кстати, если у класса нет виртуальных методов или вообще нет методов — все равно этот параметр есть. Он используется RTL-библиотекой для получения полной инфы о структуре класса. Эта инфа записана непосредственно ПЕРЕД тем адресом, на который указывает адрес VMT.

Классы #2

bdr> <.. не с потолка же я взял этот механизм ..
bdr>  А можно поинтересовать как ты к этому пришел.
bdr>  Мне лично не понятно как вообще компилятор это пропускает.
bdr>  Ведь в STRING может и не лежать адрес класса, а приведение к указателю
bdr> класса неизвестно чего

А это уже твои проблемы! Я когда-то уже писал, в Кларионе можно сделать очень много разных нестандартных вещей. Практически как в С. Но и ответственность за неправильное их использование ложиться уже на программиста.

bdr>  должно отлавливаться компилятором.
bdr>  Это что глюк или «расширение» возможностей клариона.

Это — обычное поведение компилятора. Конструкцию типа:

Ref &= (Var)

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

bdr>  Сразу возникает вопрос, а чего еще можно вытворять в кларионе подобного.
bdr> Александр Бирюков

А чего надо?:)

Классы

bdr>  Если первый пример мне понятен, то со вторым:
bdr> <Ещё, Если Вы хотите загружать динамически какие то библиотеки то не
bdr> <используёте LoalLibrary, а используйте Cla$LoadLibrary, прототипы те-же

Угу. Только чуточку по другому:

  MAP
    LoadLib(LONG _LibNameAddr),LONG,NAME('Clw$LoadLibrary')
  END

_LibNameAddr — адрес строки CSTRING с именем библиотеки.

bdr> А можно хоть небольшой комментарий.
bdr> Что это дает?
bdr> Александр Бирюков

По большому счету, эта — простая надстройка над API-шной LoadLibrary:

  • сохраняет регистры CX,DX
  • забрасывает адрес строки из AX в стек
  • вызывает LoadLibraryA
  • востанавливает DX,CX
  • возвращает AX

По своему опыту общения с Кларионом на низком уровне могу сказать, что Кларион ОЧЕНЬ чувствителен к регистрам. Поэтому, когда идет вызов ЛЮБОЙ процедуры, компилятор предполагает, что после возврата из этой процедуры все основные регистры (BX,CX,DX,SI,DI) имеют тоже значение, что и до вызова. Не «закладывается» компилятор только на AX, и то, только для функций, которые возвращают результат через AX.

Я таким образом уже несколько раз «влетал» со своими asm-процедурами. Пока не стал сохранять/восстанавливать регистры, постоянно «лезли» какие-то левые глюки.  Удалось «вычислить» только после кропотливого анализа сбойного участка с откатом назад, до места-источника проблем.

В принципе, компилятор должен учитывать, что процедуры с атрибутами C или PASCAL могут иметь другое поведение по отношению к регистрам. Но, очевидно, иногда где-то он все-таки «прокалывается» или вообще не принимает этого в расчет.

Так что, если по хорошему, то стоит для всех используемых API-шных процедур писать кларионовскую процедуру-надстройку, в которой будет только вызов API-шной процедуры. При генерации Кларионовской процедуры компилятор сам анализирует, какие используются в процедуре регистры и их сохраняет/восстанавливает.

Over-field in List-queue

Que     QUEUE
Name      STRING(40)
Key       STRING(10),OVER(Name)
Desc      STRING(100)
Price     DECIMAL(11,2)
        END

?List{PROP:Use} = Que

  1. List not «sees» OVER-field QUE.Key
  2. List considers, that the field Desc has number 2, and field Price has number 3.
  3. ?List{PROPLIST:FieldNo,2} return 2.    ! column for Desc field Who(Que,2) return «Key».
  4. Sort(Que,+Que:Desc); Sort(Que,-Que:Desc)
  5. We delete all records from the list. Sometimes we obtain GPF.
  6. End program or procedure — Always we obtain GPF.

CallBack Procedure

VS> Что-то я в силу несколько усеченных познаний в буржуйском языке плохо представляю  из Help’а, как работать с CALLBACK(entity, FileCallBackInterface, [flag]). Если у кого-то есть готовый пример, а еще лучше — пример под шаблоны Вадима Синявского, буду весьма признателен.

Так а в чем проблема-то? В доке кажется нормально все описано. Да и пример даже есть. Тоже нормальный.

Ну, на всякий случай:

Все знают, что работа в Кларион с файлами организована через драйвера. Драйвер — это обычная процедура, которая принимает все обращения к файлу. Эта процедура является как бы диспетчером, когда для выполнения определенной операции управление передается уже конкретной подпрограмме, входящей в драйверный модуль вместе с процедурой-диспетчером. Возможно не все знают, но работа с View так-же организована через процедуру-драйвер VIEWDRIVER, входящей в рантайм — библиотеку Клариона. Читать далее