Все больше и больше убеждаюсь, что Кларион очень похож на своего родителя — С. При некоторой сноровке можно сделать практически все, что может С, но при этом, как и в С, надо быть очень осторожным!
Итак.
Казалось-бы вполне стандартная и документированная декларация функции:
1. Tst(),*STRING,PROC
Т.е., функция возвращает указатель на строку (&STRING), но при этом допускается вызывать ее как обычную процедуру, без приема возвращаемого результата. В общем — ничего необычного.
Не тут-то было!
Компилятор генерит неправильный код при вызове данной функции как процедуры. В этом случае генерится код как для функции типа:
2. Tst(),STRING,PROC
А между этими двумя декларациями очень большая разница. Дело в том, что при второй декларации результат возвращается через внутренний стек Клариона, предназначенный для работы со строками. Если же функция (вторая декларация) вызывается как процедура, то компилятор генерит после нее вызов внутренней функции, которая «снимает» с внутреннего стека невостребованную строку-результат. А так, как при первой декларации результат возвращается не через стек, то после вызова такой функции как процедуры получаем разрушенный стек, что неминуемо приведет к GPF!
Поэтому настоятельно рекомендую не использовать в первой декларации атрибут PROC. А когда результат вызова данной функции не нужен, то используйте хотя бы вызов типа:
if Tst().
Кстати, на всякий случай надо иметь ввиду, что упомянутый внутренний стек для работы со строками рассчитан на 32 ячейки. Это значит, что невозможно передать в процедуру одновременно более 32 строковых параметров. Это, однако, не накладывает ограничения на кол-во последовательных вызовов или глубину рекурсии процедур со строковыми параметрами. Дело в том, что сразу же после входа в процедуру все переданные параметры «снимаются» со стека, освобождая его. Кстати, необязательные (OMITTED) строковые параметры все равно заносятся в стек даже если они не заданы при вызове.
Гипотетическая проблема с переполнением стека может возникнуть если в нескольких потоках ОДНОВРЕМЕННО будут вызваны процедуры со строковыми параметрами. При этом кол-во таких параметров в этих процедурах суммируется. Дело в том, что этот строковый стек является глобальным для всех процедур во всех потоках.
Этот стек, кстати, потенциальный источник проблем, когда в одном потоке идет его наполнение параметрами а в другом потоке, в это время, идет «снятие» параметров с него. При этом процедуры вполне могут получить «не свои» параметры.
И еще одно.
Источником трудно локализуемых проблем также может стать процедура с такой декларацией:
Tst(<*? _Param>)
P.S. Надо иметь ввиду, что все вышеописанные проблемы очень трудно «отловить» даже с помощью отладчика. Дело в том, что довольно часто такие «проблемные» места под отладчиком проходят нормально!
P.S.S. Все вышеописанные проблемы не являются чисто теоретическими. Они «отловлены» на практике из реального рабочего кода. Надо также иметь ввиду, что данные ошибки обладают способностью «отложенного» воздействия. Т.е. они могут проявится совершенно в другом месте, никак не связанном с ошибочной процедурой. Например ошибка строкового стека очень часто дает о себе знать при операциях форматирования или вывода через Message().