Большинство символьных переменных в RPG действительно имеют фиксированную длину. Что означает конечную длину. Символ, определенный как 50a, всегда будет содержать ровно 50 символов. Оценка myChar = 'A'; приведет к тому, что myChar будет содержать 50 символов: букву A, за которой следуют 49 пробелов. Это скучно, но важно.
Второй скучный, но важный момент — понять, что память выделяет вызывающая программа, а не вызываемая. Если вызывающий объект объявляет myChar 50a, а вызываемый объект объявляет myParm 65535a, вызывающий объект инициализирует только 50 байт памяти. Если вызываемый объект пытается работать с myParm дальше 50-го байта, он работает с хранилищем, состояние которого неизвестно. Как говорится, могут быть непредсказуемые результаты.
Это предыстория вашего вопроса о подпроцедуре, обрабатывающей символьные переменные, размер которых заранее не известен подпроцедуре. Классический способ справиться с этим — передать не только символьную переменную, но и ее длину. eval myProcedure(myChar: %len(myChar)); Это довольно некрасиво и заставляет каждого вызывающего абонента вычислять длину myChar. Конечно, было бы неплохо, если бы подпроцедура могла опрашивать входящий параметр, чтобы узнать, как его определил вызывающий.
IBM предоставила именно такую возможность с помощью того, что они называют операционными дескрипторами. С операционными дескрипторами вызывающий объект передает метаданные о символьном параметре вызываемому объекту. Его можно получить через CEEDOD а> API. Пример использования CEEDOD здесь.
По сути, подпроцедура должна объявить, что ей нужны операционные дескрипторы:
dddeCheck pr n opdesc
d test 20a const options(*varsize)
Затем вызывающая сторона делает обычный вызов подпроцедуры:
if ddeCheck(mtel) = *on; // 10 bytes
...
endif;
if ddeCheck(mdate: *on) = *on; // 6 bytes
...
endif;
Обратите внимание, что вызывающий объект передает в подпроцедуру переменные фиксированной длины разного размера.
Подпроцедура должна использовать CEEDOD для запроса длины входящего параметра:
dddeCheck pi n opdesc
d test 20a const options(*varsize)
...
dCEEDOD pr
d parmNum 10i 0 const
d descType 10i 0
d dataType 10i 0
d descInfo1 10i 0
d descInfo2 10i 0
d parmLen 10i 0
d ec 12a options(*omit)
d parmNum s 10i 0
d descType s 10i 0
d dataType s 10i 0
d descInfo1 s 10i 0
d descInfo2 s 10i 0
d parmLen s 10i 0
d ec s 12a
...
CEEDOD (1: descType: dataType: descinfo1: descinfo2: parmlen: *omit);
В этот момент parmlen содержит длину, которую вызывающая сторона определила для входящей переменной. Теперь мы должны что-то сделать с этой информацией. Если мы обрабатываем символ за символом, нам нужно сделать что-то вроде этого:
for i = 1 to parmLen;
char_test = %subst(test: i: 1);
...
endfor;
Если мы обрабатываем одну строку, нам нужно сделать что-то вроде этого:
returnVar = %xlate(str_lc_letters_c: str_uc_letters_c: %subst(s: 1: parmLen));
Важно никогда не ссылаться на входной параметр, если эта ссылка каким-либо образом не ограничена фактической длиной переменной, определенной вызывающей стороной. Эти меры предосторожности необходимы только для переменных фиксированной длины. Компилятор уже знает длину символьных переменных переменной длины.
Что касается того, как компилятор отображает myFixed в myVarying через CONST, поймите, как это работает. Компилятор скопирует все байты из myFixed в MyVarying — все до единого. Если myFixed равно 10a, myVarying станет длиной 10 байт. если myFixed равно 50a, то myVarying станет длиной 50 байт. Конечные пробелы всегда включаются, потому что они являются частью каждой символьной переменной фиксированной длины. Эти пробелы не очень важны для процедуры перевода, которая игнорирует пробелы, но они могут быть важны для процедуры, которая центрирует строку. В этом случае вам нужно будет прибегнуть к операционным дескрипторам или сделать что-то вроде upperVary = str_us(%trimr(myFixed));
person
Buck Calabro
schedule
06.03.2013