SAP/Abap
[Modularization] Subroutine
i009727
2022. 1. 17. 11:01
[Subroutine]
특정 스크립트의 모듈화, 구조화를 통한 재사용을 목적으로 구성된 구문으로 프로그램 내/외부에서 모두 호출이 가능하다.
* Subroutine 호출부
PERFORM write_data.
* Subroutine 정의부
FORM write_data.
WRITE :/ 'Subroutine TEST'.
ENDFORM.
[Subroutine: Parameter(1)]
Parameter: Subroutine의 정의부와 호출부 사이에서 주고받는 데이터로 모든 data type, field symbol, internal table 등이 사용 가능하며, subroutine 내에서는 일반적인 local 변수처럼 사용된다.
- Actual parameter: Subroutine 호출 시 사용되는 parameter.
- Formal parameter: Subroutine에서 사용되는 parameter.
0) Parameter의 전달방법
call-by-value | actual parameter와 formal parameter가 서로 다른 메모리 영역에 존재 => value 복사 개념 |
call-by-reference | actual parameter와 formal parameter가 서로 같은 메모리 영역에 존재 => object 참조 개념 |
call-by-value_and_result | actual parameter와 formal parameter가 서로 다른 메모리 영역에 존재, 작업 성공시에만 값 반환 |
1) Call-by-Value
PERFORM subr [USING | CHANGING] <ap>
FORM subr USING VALUE(<fp>)
REPORT ZA04_02.
DATA : gv_val TYPE c LENGTH 20 VALUE 'Call by Value test'.
PERFORM call_byvalue USING gv_val.
WRITE :/ 'gv_val: ', gv_val.
FORM call_byvalue USING VALUE(p_val).
WRITE :/ 'p_val: ', p_val.
p_val = 'Data changed'.
WRITE :/ 'p_val: ', p_val.
ENDFORM.
2) Call-by-Reference
PERFROM subr [USING | CHANGING] <ap>
FORM subr [USING | CHANGING] <fp>
REPORT ZA04_03.
DATA : gv_val TYPE c LENGTH 30 VALUE 'Call by Reference test'.
PERFORM call_byref CHANGING gv_val.
WRITE :/ 'gv_val: ', gv_val.
FORM call_byref CHANGING p_val.
WRITE :/ 'p_val: ', p_val.
p_val = 'Data changed'.
WRITE :/ 'p_val: ', p_val.
ENDFORM.
3) Call-by-Value_and_Result
Call-by-reference와 비슷한 의미를 갖는다.
해당 subroutine이 정상종료될 경우 actual parameter값이 formal parameter값으로 변경된다.
PERFORM subr [USING | CHANGING] <ap>
FORM subr CHANGING VALUE(<fp>)
REPORT ZA04_04.
DATA : gv_val1 TYPE i VALUE 2,
gv_val2 TYPE i VALUE 3,
gv_sum TYPE i.
PERFORM sum_data USING gv_val1 " call_by_value
gv_val2 " call_by_value
CHANGING gv_sum. " call_by_reference
WRITE :/ 'Result: ', gv_sum.
FORM sum_data USING value(p_val1) " call_by_value
value(p_val2) " call_by_value
CHANGING value(p_sum). " call_by_value_and_result
p_sum = p_val1 + p_val2.
ENDFORM.
4) 정리
- PERFORM(호출부) 구문에서는 USING, CHANGING이 모두 사용 가능하지만, 의미를 다르게 하기위해 나눠 사용한다.
- USING: 값을 사용한다는 의미 => 메모리가 별도로 존재
- CHANGING: 값을 변경한다는 의미 => 동일한 메모리를 참조
- FORM(정의부) 구분에서는 USING VALUE, CHANGING VALUE에 따라 동작이 달라진다.
- USING VALUE (<fp>): formal parameter의 메모리가 할당되고, 값이 복사되어 전달받는다. => Call-by-Value
- CHANGING VALUE (<fp>): actual parameter의 주소값을 전달받는다. => Call-by-Reference | Call-by-ValueAndResult
[Subroutine: Parameter(2)]
1) Parameter 타입 정의
FORM 구문 내의 formal parameter는 TYPE, LIKE구문을 사용해 모든 abap data type을 사용할 수 있다.
- Generic type: 특정 type을 명시적으로 지정하지 않으면 actual parameter의 기술적 속성을 상속받게 된다.
- 이때는 actual param, formal param 간의 type conversion이 가능해야한다.
# 1. Generic type 사용
FORM subr CHANGING p_val.
# 2. Abap 기본 data type 사용
FORM subr CHANGING p_val TYPE d.
# 3. Actual parameter와 같은 type 사용.
FORM subr CHANGING p_val LIKE gv_val.
- Generic type을 사용할때, actual param, formal param간 type conversion이 불가능하다면 구문에러가 발생한다.
REPORT ZA04_05.
DATA : gv_val TYPE c. " actual param: Type c
PERFORM call_byref CHANGING gv_val.
WRITE :/ gv_val.
FORM call_byref CHANGING p_val TYPE i. " formal param: Type i
p_val = 'Value is changed'.
ENDFORM.
2) Parameter와 구조체
FORM subr CHANGING <param> [TYPE | LIKE | STRUCTURE] <str>
REPORT ZA04_06.
DATA : BEGIN OF gs_str,
col1 VALUE 'A',
col2 VALUE 'B',
END OF gs_str.
PERFORM write_data CHANGING gs_str.
FORM write_data USING ps_str STRUCTURE gs_str.
WRITE : ps_str-col1, ps_str-col2.
ENDFORM.
- Parameter type으로 구조체를 사용할때 명시적으로 type을 지정하지 않으면 구조체 내 컬럼을 찾을수 없으므로 에러가 발생한다.
- 이후 배울 필드 심볼을 통해 명시적 type 지정 없이 사용이 가능하다.
3) Paramter와 인터널 테이블
- USING 및 CHANGING 구문: TYPE 및 LIKE를 사용한다.
FORM subr CHANGING <param> [TYPE | LIKE] <itab>
REPORT ZA04_07.
TYPES : BEGIN OF t_str,
col1 TYPE c,
col2 TYPE i,
END OF t_str.
TYPES : t_itab TYPE TABLE OF t_str.
DATA : gs_str TYPE t_str,
gt_itab TYPE t_itab.
gs_str-col1 = 'A'.
gs_str-col2 = 1.
APPEND gs_str TO gt_itab.
gs_str-col1 = 'B'.
gs_str-col2 = 2.
APPEND gs_str TO gt_itab.
PERFORM test_itab USING gt_itab.
FORM test_itab USING pt_itab TYPE t_itab.
READ TABLE pt_itab WITH KEY col1 = 'A' INTO gs_str.
IF sy-subrc EQ 0.
WRITE :/ gs_str-col1, gs_str-col2.
ENDIF.
ENDFORM.
- TABLES 구문: 이전 프로그램에서 USING, CHANGING 대신 사용되던 구문이지만 호환성 문제로 현재도 사용되고 있다.
REPORT ZA04_08.
TYPES : BEGIN OF t_str,
col1 TYPE c,
col2 TYPE i,
END OF t_str.
TYPES : t_itab TYPE TABLE OF t_str.
DATA : gt_itab TYPE t_itab.
PERFORM test_itab TABLES gt_itab.
PERFORM write_data TABLES gt_itab.
FORM test_itab TABLES pt_itab TYPE t_itab.
DATA ls_str TYPE t_str.
ls_str-col1 = 'A'.
ls_str-col2 = 1.
APPEND ls_str TO pt_itab.
ls_str-col1 = 'B'.
ls_str-col2 = 2.
APPEND ls_str TO pt_itab.
ENDFORM.
FORM write_data TABLES pt_itab LIKE gt_itab.
DATA ls_str TYPE t_str.
LOOP AT pt_itab INTO ls_str.
WRITE :/ ls_str-col1, ls_str-col2.
ENDLOOP.
ENDFORM.
[Subroutine 호출]
Subroutine은 internal / external 두가지 방식으로 호출이 가능하다.
# 1. Internal call.
PERFORM subr.
# 2. External call
PERFORM subr (prog) [IF FOUND]
1) Internal subroutine call
REPORT ZA04_09.
DATA : gv_val1(10) TYPE c VALUE 'Enjoy',
gv_val2(10) TYPE c VALUE 'Abap',
gv_val3(20) TYPE c.
PERFORM concate_string USING gv_val1 gv_val2
CHANGING gv_val3.
FORM concate_string USING VALUE(p_val1) VALUE(p_val2)
CHANGING VALUE(p_val3).
CONCATENATE p_val1 p_val2 INTO p_val3 SEPARATED BY space.
PERFORM write_data USING p_val3.
ENDFORM.
FORM write_data USING VALUE(p_val).
WRITE :/ p_val.
ENDFORM.
2) External subroutine call
- 명시된 프로그램에 subroutine이 존재하지 않는다면 덤프에러가 발생한다.
- [IF FOUND] 키워드 추가시 해당 프로그램에 해당 subroutine이 존재하는 경우에만 실행한다.
- 해당 프로그램에 해당 subroutine이 존재하지 않으면 해당 subroutine은 호출되지 않는다.
REPORT ZA04_10.
DATA : gv_val1(10) TYPE c VALUE 'External',
gv_val2(10) TYPE c VALUE 'Call',
gv_val3(20) TYPE c.
PERFORM concate_string(ZA04_09) IF FOUND USING gv_val1 gv_val2
CHANGING gv_val3.
3) Subroutine 동적 호출
- Subroutine name 동적 설정
- 동적 호출시에는 프로그램명과 서브루틴명을 반드시 대문자로 지정해야 한다.
PERFORM (fsubr) [IN PROGRAM (fprog)] ...
REPORT ZA04_11.
DATA : gv_first(10) TYPE c VALUE 'Dynamic',
gv_second(10) TYPE c VALUE 'Call',
gv_result(20) TYPE c.
DATA : gv_pname(10) TYPE c VALUE 'za04_09',
gv_sname(20) TYPE c VALUE 'concate_string'.
TRANSLATE gv_pname TO UPPER CASE.
TRANSLATE gv_sname TO UPPER CASE.
PERFORM (gv_sname) IN PROGRAM (gv_pname) IF FOUND
USING gv_first gv_second
CHANGING gv_result.
- LIST를 이용한 Subroutine 호출
- 시스템의 index 순서에 따라 subroutine을 호출한다.
- 오직 Internal subroutine call만 가능하며 parameter 사용이 불가능하다.
PERFORM idx OF subr1 subr2 ...
REPORT ZA04_12.
DO 2 TIMES.
PERFORM sy-index OF subr1 subr2.
ENDDO.
FORM subr1.
WRITE :/ '1 subroutine is called'.
ENDFORM.
FORM subr2.
WRITE :/ '2 subroutine is callled'.
ENDFORM.
#) Abap 언어의 반복문
3가지 형태의 반복문이 있으며 각 반복문에서의 반복횟수는 SY-INDEX 시스템 변수에 저장된다.
1. DO ~ ENDDO 구문
DO n TIMES.
...
ENDDO.
2. WHILE ~ ENDWHILE 구문
WHILE flag = 'X'.
...
ENDWHILE.
3. LOOP ~ ENDLOOP 구문
LOOP AT itab TO wa.
...
ENDLOOP.
[Subroutine 종료]
일반적으로 Subroutine은 ENDFORM문을 만나면 정상 종료되지만, EXIT 또는 CHECK구문을 통해 서브루틴 수행중에 종료할 수 있다.
- EXIT구문: Subroutine을 바로 종료시킨다.
- CHECK구문: 해당 구문이 거짓이면 Subroutine을 종료시킨다.
REPORT ZA04_13.
PARAMETERS : p_val TYPE char10.
PERFORM end_subr USING p_val.
FORM end_subr USING VALUE(p_val).
CASE p_val.
WHEN 'EXIT'.
WRITE :/ 'Subroutine exit'.
EXIT.
WHEN 'CHECK'.
WRITE :/ 'Value check'.
CHECK p_val NE 'CHECK'.
WHEN OTHERS.
ENDCASE.
WRITE :/ 'Subroutine is nomally end'.
ENDFORM.
#) Abap 프로그램의 분기문
1. IF ~ ENDIF 분기문
IF 조건문.
...
[ELSEIF 조건문].
...
ELSE.
...
ENDIF
2. CASE ~ ENDCASE 분기문
CASE 변수.
WHEN 'val1'.
...
WHEN 'val2'.
...
WHEN OTHERS.
...
ENDCASE.
[Temporary Subroutine]
실행중인 프로그램의 메모리에 Subroutine pool을 생성하여 동적으로 subroutine을 정의할 수 있다.
GENERATE SUBROUTINE POOL <itab> NAME <prog>
- <itab>: 생성할 subroutine의 소스코드를 삽입할 인터널 테이블
- <prog>: 생성할 subroutine 프로그램
- 사용자의 입력에 따라 인터널테이블을 동적으로 생성해야 하는 경우 유용하게 사용된다.
- ex) 매출 조회 프로그램: 입력일에 따라 조회할 데이터(인터널테이블)가 달라진다.
REPORT ZA04_14.
DATA : gt_code(72) OCCURS 10,
gs_code(72),
gv_prog(8),
gv_msg(120).
APPEND 'PROGRAM SUBPOOL.' TO gt_code.
APPEND 'FORM dynamic_subr.' TO gt_code.
APPEND 'WRITE / ''Dynamic Subroutine is called''.' TO gt_code.
APPEND 'ENDFORM.' TO gt_code.
LOOP AT gt_code INTO gs_code.
WRITE :/ 'gs_code: ', gs_code.
ENDLOOP.
GENERATE SUBROUTINE POOL gt_code NAME gv_prog MESSAGE gv_msg.
IF sy-subrc <> 0.
WRITE :/ 'Subroutine POOL is failed'.
ELSE.
WRITE :/ 'Subroutine POOL name: ', gv_prog.
SKIP 1.
PERFORM dynamic_subr IN PROGRAM (gv_prog).
ENDIF.
[Perform on Commit]
Subroutine 호출 시 ON [COMMIT | ROLLBACK] 옵션을 사용하면 [COMMIT WORK | ROLLBACK WORK] 구문을 만날때 해당 FORM이 실행된다.
- PERFORM insert_data ON COMMIT 명령어가 실행되면
- insert_data subroutine이 실행되지 않는다.
- 이후 COMMIT WORK 명령어가 실행되야 insert_data subroutine이 실행된다.
REPORT ZA04_15.
DATA : gs_scarr LIKE scarr,
gv_flg TYPE c.
SELECT SINGLE *
FROM scarr
INTO gs_scarr
WHERE carrid = 'AA'.
WRITE :/ 'delete_data call'.
PERFORM delete_data USING gs_scarr.
WRITE :/ 'insert_data call ON COMMIT'.
PERFORM insert_data ON COMMIT.
IF gv_flg = 'X'.
WRITE :/ 'COMMIT WORK'.
COMMIT WORK.
ENDIF.
FORM delete_data USING value(ps_scarr) TYPE scarr.
WRITE :/ 'delete_data called'.
DELETE scarr FROM ps_scarr.
IF sy-subrc EQ 0.
gv_flg = 'X'.
ENDIF.
ENDFORM.
FORM insert_data.
WRITE :/ 'insert_data called'.
INSERT scarr FROM gs_scarr.
ENDFORM.