Anthony Cecchini is the President of Information Technology Partners (ITP), an SAP consulting company headquartered in Pennsylvania. ITP offers comprehensive planning, resource allocation, implementation, upgrade, and training assistance to companies. Anthony has over 20 years of experience in SAP R/3 business process analysis and SAP systems integration. His areas of expertise include SAP NetWeaver integration; ALE development; RFC, BAPI, IDoc, Dialog, and Web Dynpro development; and customized Workflow development. You can reach him at [email protected].
ABAP Dynamic Programming Techniques
OK, lets pick up from last month where I promised we would look at how to use the Runtime Type Identification (RTTI) technique to get information at runtime about data that is passed with generic types. So lets get started…
Runtime Type Identification (RTTI)
Runtime Type Identification (RTTI) is a powerful technique for obtaining all information about a data type at runtime. When you use dynamic programming techniques, sometimes you need to dynamically determine the data type or properties in order to decide how to handle the data. This situation typically occurs when you use generic types, where you need to obtain at runtime the missing data characteristics not described by the generic type. For example, if you use the generic type ANY in a subroutine, you will need to get information at runtime about the type of the data being passed.
RTTI is implemented in ABAP Objects using description objects. Description objects for types are created from description classes, and every type in the ABAP hierarchy has a corresponding description object. Next, every description class has special attributes and appropriate navigation methods. For instance, the class CL_ABAP_TABLEDESCR has an attribute TABLE_KIND and a navigation method GET_TABLE_LINE_TYPE.
In the RTTI class hierarchy. As you can see below, the class CL_ABAP_TYPEDESCR is the root class, which contains all methods to derive a description object from a data type or the name of the type.
The class CL_ABAP_TYPEDESCR provides four methods to derive a description object for a type:
DESCRIBE_BY_NAME: This method takes a name of a type as an input parameter and returns an object reference to the corresponding description object.
DESCRIBE_BY_DATA: This method takes data as input and returns an object reference to the description object of the data type.
DESCRIBE_BY_DATA_REF: This method takes a data reference as input and returns an object reference to the description object of the data type for the data pointed to by the reference.
DESCRIBE_BY_OBJECT_REF: This method takes an object reference as input and returns an object reference to the description object of the object type for the object pointed to by the reference.
You can obtain a reference to a description object for a type in different ways (e.g., by name, by data, with navigation, etc.). Now this is important – RTTI guarantees that exactly one description object exists for every type. All references point to the same description object, no matter which path was chosen to retrieve it.
So lets look at an example. Let’s use RTTI to guarantee a “STRICT” Parameter check. ABAP performs only a technical parameter check. In other words, types with the same features are considered to be compatible, even if they have different names. What if a method relies on a passed parameter being typed with a specific type because of something other than it’s technical compatibility? This cannot be expressed in ABAP.
Take a look at the code below….
PROGRAM rtti_typecheck_example. TYPE-POOLS abap. *----------------------------------------------------------------------* * CLASS lcl_typecheck DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_typecheck DEFINITION. PUBLIC SECTION. METHODS: constructor IMPORTING p_type_name TYPE string EXCEPTIONS type_not_found, check IMPORTING p_x TYPE any RETURNING value(p_flag) TYPE abap_bool. PRIVATE SECTION. DATA: rtti_ref TYPE REF TO cl_abap_typedescr. ENDCLASS. "lcl_typecheck DEFINITION *----------------------------------------------------------------------* * CLASS lcl_typecheck IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_typecheck IMPLEMENTATION. METHOD constructor. CALL METHOD cl_abap_typedescr=>describe_by_name EXPORTING p_name = p_type_name RECEIVING p_descr_ref = rtti_ref EXCEPTIONS type_not_found = 4. IF sy-subrc <> 0. RAISE type_not_found. ENDIF. ENDMETHOD. "constructor METHOD check. DATA check_ref TYPE REF TO cl_abap_typedescr. CALL METHOD cl_abap_typedescr=>describe_by_data EXPORTING p_data = p_x RECEIVING p_descr_ref = check_ref. IF check_ref = rtti_ref. p_flag = abap_true. ELSE. p_flag = abap_false. ENDIF. ENDMETHOD. "check ENDCLASS. "lcl_typecheck IMPLEMENTATION TYPES: my_special_type TYPE HASHED TABLE OF i WITH UNIQUE KEY table_line. *----------------------------------------------------------------------* * CLASS lcl_app DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_app DEFINITION. PUBLIC SECTION. CLASS-METHODS except_only_my_special_type IMPORTING p_x TYPE any. ENDCLASS. "lcl_app DEFINITION *----------------------------------------------------------------------* * CLASS lcl_app IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_app IMPLEMENTATION. METHOD except_only_my_special_type. DATA my_type TYPE REF TO lcl_typecheck. CREATE OBJECT my_type EXPORTING p_type_name = 'MY_SPECIAL_TYPE'. IF my_type->check( p_x ) = abap_true. WRITE / 'Typecheck OK'. ELSE. WRITE / 'Typecheck ERROR'. ENDIF. ENDMETHOD. "except_only_my_special_type ENDCLASS. "lcl_app IMPLEMENTATION DATA: my_special_data TYPE my_special_type, some_other_data TYPE HASHED TABLE OF i WITH UNIQUE KEY table_line. START-OF-SELECTION. CALL METHOD lcl_app=>except_only_my_special_type EXPORTING p_x = my_special_data. CALL METHOD lcl_app=>except_only_my_special_type EXPORTING p_x = some_other_data.
Lets take a deeper dive into the code to understand why and what it is doing…
The CONSTRUCTOR method takes the type name to check for as input. After creating an object of LCL_TYPECHECK and passing the type name, the method CHECK is called. It checks whether the data type of the passed data P_X is the same as the type passed at the creation of the CHECK object. Remember P_X was typed with the Generic Type ANY.
In the CONSTRUCTOR method, the object reference to the description object of the passed type is stored in the private instance attribute RTTI_REF. The method CHECK gets the description object of the data type. Because RTTI guarantees that only one description object exists for every type, the type check itself consists only of one object reference comparison. If the data passed to the method CHECK is of the same type as the description object pointed to in the attribute RTTI_REF, the method DESCRIBE_BY_DATA returns a reference to the same description object. The CHECK method either returns ABAP_TRUE or ABAP_FALSE, depending on the success of the type check.
The program calls the example application class twice. The first time, it is called with the variable MY_SPECIAL_DATA, which is typed with MY_SPECIAL_TYPE. The second time, it is called with the variable SOME_OTHER_DATA. While both variables have the same technical type (HASHED Table of i), the second variable was not typed with the type MY_SPECIAL_TYPE. Therefore, the application writes a success message for the first case and an error message for the second case. This is why I am calling this a “STRICT” Type check.
We now have discussed all the important concepts needed for dynamic programming in ABAP. We began with techniques for accessing data dynamically, starting with generic types and proceeding to the dynamic data types of internal tables and strings. Next, we explored techniques for executing operations dynamically using various forms of dynamic token specification. And we have just discussed using RTTI for dynamically obtaining parameter characteristics. But sometimes even all these techniques are not sufficient to solve some problems. As a last resort, a really, really, last resort, you might want to consider dynamic code generation and execution. Thats right, build a subroutine or entire program at run-time and execute it. This we will cover in the final blog on this series.