QUEUE в качестве параметра

OAR>> А что здесь непонятно?!
OAR>> QueRef обьявлен как УКАЗАТЕЛЬ НА ОЧЕРЕДЬ.
OAR>> При передаче его как ANY компилятор предполагает, что
OAR>> в процедуру передается БУФЕР ЗАПИСИ ОЧЕРЕДИ на которую
OAR>> указывает QueRef.
OAR>> И, соответственно, его действия следующие:
OAR>> — по указателю QueRef читает заголовок очереди, на которую
OAR>> ссылается QueRef
OAR>> — из заголовка выбирает указатель на буфер записи этой
OAR>> очереди
OAR>> — по выбраному указателю создает UFO-обьект типа GROUP
OAR>> — передает его в процедуру

AA> …
AA> QueRef &= NULL
AA> MyProc(QueInst, QueRef)
AA> …

AA> каков будет результат? QueRef ведь ИНИЦИАЛИЗИРОВАН???

OAR>> Как видно, уже на первом шаге произойдет GPF, т.к. перед
OAR>> вызовом процедуры QueRef НЕ ИНИЦИАЛИЗИРОВАН, т.е.,
OAR>> грубо говоря, QueRef &= NULL!!!

AA> ну ежели компилятору самому в облом инициализировать указатели
AA> каким-либо значением (пусть даже NULL), то уж как-то он (runtime)
AA> должен отслеживать валидность указателей…

Я, очевидно, неверно обьяснил.
Под ИНИЦИАЛИЗАЦИЕЙ понимается присвоение указателю значения ОТЛИЧНОГО от нуля, т.е. от NULL.
Я же написал, что согласно принятой схеме превращения указателя на очередь в ANY-обьект, сначала производится чтение заголовка очереди на которую ссылается QueRef. А так как QueRef &= NULL, то, соответственно, получим чтение из ячейки памяти с НУЛЕВЫМ АДРЕСОМ, что, естественно, приведет к GPF!

А о том, что у Клариона проблемы с проверкой валидности указателей, я уже давно и неоднократно писал. По моему мнению, именно такое отношение к обработке указателей является причиной ~80%-90% ошибок Клариона! Хотя, вообще-то, это не столько ошибки Клариона, сколько ошибки C-библиотеки, на которой базируется ядро RTL, и ошибки компилятора, которым оттранслировано это ядро. Ну и, естественно, небрежное или невнимательное отношение самих разработчиков к данной проблеме.

Казалось-бы, чего проще — ВСЕГДА при входе в процедуру сразу проверять переданные указатели на валидность? И, кроме этого, попутно делать такие проверки непосредственно ПЕРЕД обращением к ячейке памяти. Типа:

  if QueRef &= Null then Return.
  ...
  Peek(QueRefAddr,QueHeader)
  if ~QueHeader.BuffPtr then Return.

OAR>> Надеюсь, что я понятно обьяснил данную ситуацию?

AA> ага…только как быть в случае, если я хочу инициализировать
AA> QueRef внутри MyProc(…) ???

Ну, здесь надо немного по другому. Так сказать, необходимо обмануть слишком интеллектуальный компилятор Клариона. Благо — он (компилятор) довольно легко поддается обману:)

  MAP
    MyProc(*QUEUE _Que),LONG
  END

  Code
  QueRef &= MyProc(QueInst)
  Return

MyProc  PROCEDURE(*QUEUE _Que)

RefGrp  GROUP
qRef      &QUEUE
        END
lRef    LONG,OVER(RefGrp)

  Code
  RefGrp.qRef &= _Que
  Return(lRef)

Вот теперь, после отработки процедуры, реферал QueRef будет ссылаться на очередь QueInst.

AA> Я понимаю, что это — «особенности» Клариона :))
AA> Обиднааа тока когда на них наступаешь… 🙁

Да, это именно ОСОБЕННОСТИ (без кавычек) языка Кларион! У него, как впрочем и у других языков, есть свои особенности, которые необходимо знать и учитывать. Другое дело, что многие такие особенности или совсем не документированы, или документированы неполно.

AA> кстати: С5ее — win32
AA> вот такая вот ситуация с передачей ссылки по ANY приводит к краху.
AA> Хотя с точки зрения компилятора никакого криминала тут нету.

    PROGRAM
    MAP
     MyProc(*QUEUE Q, *ANY F)
    .
Que QUEUE,TYPE
F1   LONG
F2   LONG
    .
QueInst Que
QueRef  &Que
 CODE
 MyProc(QueInst, QueRef)
 MESSAGE('Ok')
MyProc PROCEDURE (*QUEUE Q, *ANY F)
 CODE

! тут что-то делаем (если досюда доберемся 🙂

А что здесь непонятно?!
QueRef обьявлен как УКАЗАТЕЛЬ НА ОЧЕРЕДЬ. При передаче его как ANY компилятор предполагает, что в процедуру передается БУФЕР ЗАПИСИ ОЧЕРЕДИ на которую указывает QueRef.
И, соответственно, его действия следующие:

по указателю QueRef читает заголовок очереди, на которую ссылается QueRef из заголовка выбирает указатель на буфер записи этой очереди по выбраному указателю создает UFO-обьект типа GROUP передает его в процедуру

Как видно, уже на первом шаге произойдет GPF, т.к. перед вызовом процедуры QueRef НЕ ИНИЦИАЛИЗИРОВАН, т.е., грубо говоря, QueRef &= NULL!!!

Для нормальной работы необходимо сделать что-то типа:

  QueRef &= QueInst
  MyProc(QueInst,QueRef)

Вот теперь все отработает без ошибок и в процедуру будет передан указатель на очередь QueInst и ANY-ссылка на буфер записи этой-же очереди.

Надеюсь, что я понятно обьяснил данную ситуацию?

AA> Допустим, что где-то там имеются некие очереди

Q1 QUEUE
     UserID    LONG
     UserName  STRING(100)
   END

Q2 QUEUE
     DepartmentID  LONG
     UserID        LONG
     BossName      STRING(100)
     UserName      STRING(100)
   END

AA> Эти очереди используются локально в каких-то процедурках. И апосля
AA> того, как пользователь чего-то там поменял, нам требуется обновить БД.
AA> Логично «нарисовать», скажем:

UpdateUsersName (*QUEUE Q), BOOL
 CODE
  ! было бы хорошо использовать тута Q.UserID
  ! но компилятор понятия не имеет о полях передаваемой очереди
  ! посему вот тут и логично использовать поиск по имени поля
  ! оно-то ведь всегда есть :)

 LOOP i# = 1 TO RECORDS(Q)
   GET(Q, i#)
   IF GetFieldValueByName (Q, 'UserID', UserID#)
      AND GetFieldValueByName (Q, 'UserName', UserName)

     ! обновить(добавить) запись БД
   .
 .

AA> Вот для подобных вещей, на мой взгляд, и удобно использовать
AA> нетитпизированные очереди(группы). Фиг с ней, со структурой — главное
AA> штоб нужные поля были 🙂

Может я чего не понял — после праздников, думаю, простительно:) Или от того, что не читал начала данной темы — на довольно длительное время был «отлучен» от компа.

Вот почитал несколько писем по данной теме и подумал — а зачем, собственно, чего-то изобретать, писать какие-то функции?! А что, типизированные очереди уже отменили? Или, может, отменили наследование очередей?

Конкретно, вышеописанная задача решается с пол-пинка таким
образом:

              MAP
                UpdateUserName(Q1 _Que)
              END

Q1            QUEUE
UserID          LONG
UserName        STRING(100)
              END

Q2            QUEUE(Q1)
DepartmentID    LONG
BossName        STRING(100)
              END

  Code
  ...
  UpdateUserName(Q2)
  ...
  Return

UpdateUserName PROCEDURE(Q1 _Que)

  Code
  LOOP I# = 1 TO RECORDS(_Que)
    GET(_Que,I#)
    IF _Que.UserID AND _Que.UserName
       ! обновить(добавить) запись БД
    .
  .
  Return

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

Ну, и что здесь сложного?
А по поводу предыдущих писем по данной теме, где Андрей сетовал на какие-то трудности с обьявлением типизированных очередей, хотелось-бы услышать хотя-бы одну из них!? Тем более, что, как он уже писал, код пишеться полностью руками.

Или трудно завести что-то типа типизированной очереди Q1 с полями, которые присутствуют во всех остальных подобных очередях, и наследовать потом эти очереди от нее?

Вот, честное слово — сколько работаю с очередями — еще НИ РАЗУ не было НИКАКИХ проблем с обработкой очередей, передаваемых в качестве параметров в процедуры!

Другое дело, если речь идет об УНИВЕРСАЛЬНОМ механизме работы с очередями. Тогда можно и потеоретизировать! Хотя, как и всякий универсализм, этот подход влечет за собой довольно приличные накладные расходы на скорость выполнения. Да и с обработкой массивов будут проблемы.