Dynamic Programming & RTTS

RTTS = RTTI (Runtime Type Identification: describe types) + RTTC (Runtime Type Creation: build new types).

25.1 Runtime Type Services (RTTS)

RTTS = RTTI (Runtime Type Identification: describe types) + RTTC (Runtime Type Creation: build new types). Classes start with CL_ABAP_*DESCR:

CL_ABAP_TYPEDESCR
├── CL_ABAP_DATADESCR
│   ├── CL_ABAP_ELEMDESCR
│   ├── CL_ABAP_COMPLEXDESCR
│   │   ├── CL_ABAP_STRUCTDESCR
│   │   └── CL_ABAP_TABLEDESCR
│   └── CL_ABAP_REFDESCR
└── CL_ABAP_OBJECTDESCR
    ├── CL_ABAP_CLASSDESCR
    └── CL_ABAP_INTFDESCR

25.2 RTTI: inspecting types

DATA(lo_struct) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_data( ls_order ) ).
LOOP AT lo_struct->get_components( ) INTO DATA(ls_comp).
  out->write( |{ ls_comp-name }: { ls_comp-type->type_kind } { ls_comp-type->length }| ).
ENDLOOP.

DATA(lo_type) = cl_abap_typedescr=>describe_by_name( 'SFLIGHT' ).
DATA(lo_elem) = CAST cl_abap_elemdescr( cl_abap_typedescr=>describe_by_data( lv_amount ) ).
DATA(lt_ddic) = lo_elem->get_ddic_field( ).             " DDIC info if typed with data element
DATA(lv_class) = cl_abap_classdescr=>get_class_name( lo_obj ).

25.3 RTTC: creating types at runtime

DATA(lt_components) = VALUE cl_abap_structdescr=>component_table(
  ( name = 'ID'     type = cl_abap_elemdescr=>get_i( ) )
  ( name = 'NAME'   type = cl_abap_elemdescr=>get_string( ) )
  ( name = 'AMOUNT' type = cl_abap_elemdescr=>get_p( p_length = 8 p_decimals = 2 ) ) ).

DATA(lo_new_struct) = cl_abap_structdescr=>get( lt_components ).
DATA(lo_new_table)  = cl_abap_tabledescr=>get( p_line_type = lo_new_struct ).

DATA lr_table TYPE REF TO data.
CREATE DATA lr_table TYPE HANDLE lo_new_table.
ASSIGN lr_table->* TO FIELD-SYMBOL(<lt_dynamic>).

25.4 Generic table display from any DB table

(Production code must also do an authorization check on table access — e.g. S_TABU_NAM / S_TABU_DIS.)

PARAMETERS p_tab TYPE tabname DEFAULT 'SCARR'.

START-OF-SELECTION.
  DATA lr_data TYPE REF TO data.
  TRY.
      cl_abap_dyn_prg=>check_table_name_str( val = p_tab packages = '' ).
    CATCH cx_abap_not_a_table cx_abap_not_in_package.
      MESSAGE 'Invalid table' TYPE 'E'.
  ENDTRY.
  CREATE DATA lr_data TYPE STANDARD TABLE OF (p_tab).
  ASSIGN lr_data->* TO FIELD-SYMBOL(<lt_data>).
  SELECT * FROM (p_tab) INTO TABLE @<lt_data> UP TO 100 ROWS.
  cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) CHANGING t_table = <lt_data> ).
  lo_alv->display( ).

25.5 Dynamic calls

Dynamic calls bypass compile-time checks — catch CX_SY_DYN_CALL_ERROR (subclasses like CX_SY_DYN_CALL_ILLEGAL_METHOD) and CX_SY_CREATE_OBJECT_ERROR.

" Dynamic method call
DATA(lv_class)  = `ZCL_ORDER_SERVICE`.
DATA(lv_method) = `GET_OPEN_ORDERS`.
DATA lo_obj TYPE REF TO object.
CREATE OBJECT lo_obj TYPE (lv_class).
DATA(lt_params) = VALUE abap_parmbind_tab(
  ( name = 'IV_CUSTOMER' kind = cl_abap_objectdescr=>exporting value = REF #( lv_customer ) )
  ( name = 'RT_ORDERS'   kind = cl_abap_objectdescr=>receiving value = REF #( lt_result ) ) ).
CALL METHOD lo_obj->(lv_method) PARAMETER-TABLE lt_params.

" Static method dynamically
CALL METHOD (lv_class)=>(lv_static_method).

" Dynamic function call
CALL FUNCTION lv_fm_name EXPORTING iv_x = 1 EXCEPTIONS OTHERS = 1.

" Dynamic SUBMIT / PERFORM
SUBMIT (lv_report) AND RETURN.
PERFORM (lv_form) IN PROGRAM (lv_program) IF FOUND.

25.6 Dynamic SQL and generic code

Dynamic SELECT clauses (§15.7), dynamic WHERE/ORDER BY, dynamic sort (SORT itab BY (lv_field)), dynamic READ TABLE … WITH KEY (lv_field) = value, and dynamic LOOP … WHERE (lv_cond) (7.02+) all accept tokens at runtime.

25.7 Program generation (avoid)

GENERATE SUBROUTINE POOL and INSERT REPORT create code at runtime. They're security-sensitive, unavailable in ABAP Cloud, and almost always replaceable by RTTS and dynamic calls.