Program Flow Logic

«« Previous
Next »»

1. Expressions and Functions for Conditions


The flow control of a program is managed by control structures that are generally controlled by logical expressions. The following options exist for the formulation and analysis of conditions:

◈ Logical expressions
◈ Logical functions

In addition to control structures, there are also other statements where logical expressions and functions can be specified.

1.1 log_exp - Logical Expressions 

Syntax

...   rel_exp
    | [NOT] log_exp [AND|OR|EQUIV log_exp] ...

Effect

A logical expression formulates a condition for operands. The result of a logical expression log_exp is a truth value and can therefore be true or false.

A logical expression is either a single relational expression rel_exp or an expression constructed from the Boolean operators NOT, AND, OR, EQUIV and one or more logical expressions. Parentheses are possible here. An atomic part of a composite logical expression is always one of the following relational expressions:

◈ Comparisons
    ◈ Comparison expressions
◈ Predicates
    ◈ Predicate expressions
    ◈ Predicate functions
    ◈ Predicative method calls

Logical expressions can be used as follows:

◈ To formulate conditions in control statements and other statements used to control the program flow.
◈ in conditional expressions
◈ As an argument of a Boolean function for representing a truth value in a character-like or byte-like data object.
◈ In WHERE conditions of processing statements for internal tables.

Notes

◈ The operand positions operand of most relational expressions are general expression positions, which means that, depending on the expression, data objects, predefined functions, functional methods, calculation expressions, constructor expressions, or table expressions can be specified.
◈ If, in a logical expression, functional methods are specified as operands of a relational expression, they are executed from left to right and from inside to outside before the relational expression is evaluated. In the case of joined relational expressions, this is true for each individual relational expression and not for the overall logical expression.
◈ Since ABAP does not recognize any Boolean data objects for the truth values true and false, the result of a logical expression cannot currently be directly assigned to a data object as is the case for calculation expressions. Instead, the return value of a Boolean function can be used.
◈ Logical expressions cannot be mixed with calculation expressions. However, a logical expression can be executed as an argument of a Boolean function in a suitable calculation expression.
The program DEMO_EXPRESSIONS also shows examples of the use of logical expressions, among other things.

Example

Logical expression created from a predicate expression, a comparison expression, and a predicate function using the Boolean operator AND, in a control statement.

IF p1 IS SUPPLIED    AND
   p1 <= upper_limit AND
   matches( val = p2 regex = regular_expression ).
  ...
ENDIF.

◉ rel_exp - Comparison Expressions

Syntax

... { operand1   {=|EQ|<>|NE|>|GT|<|LT|>=|GE|<=|LE}
               | {CO|CN|CA|NA|CS|NS|CP|NP}
               | {BYTE-CO|BYTE-CN|BYTE-CA|BYTE-NA|BYTE-CS|BYTE-NS}
               | {O|Z|M} operand2 }
  | { operand [NOT] BETWEEN operand1 AND operand2 }
  | { operand [NOT] IN seltab }  ...

Effect

Comparison expressions join two or more operands using a relational operator to make a relational expression and produce a truth value as the result of the comparison.

◈ The operands are compared using one of the relational operators shown here.
◈ The comparisons follow comparison rules.

operand1, operand2, and operand are general expression positions, which means that the following can be specified:

◈ Data objects
◈ Predefined functions
◈ Functional methods
◈ Calculation expressions
◈ Constructor expressions
◈ Table expressions

When a predefined function, a functional method, or a constructor expression is specified, its return value or result is used as an operand. When a calculation expression is specified, its result is used. A single operand can be compared with all single operands listed in the comparison rules for single operands. A calculation expression can be compared with all single operands or comparisons listed in the comparison rules for calculation expressions.

◑ rel_exp - Relational Operators

Depending on the operands that are compared with each other, four groups of relational operators for relational expressions can be distinguished:

⬥ Relational operators for all data types

=, EQ, <>, NE, >, GT, <, LT, >=, GE, <=, LE

BETWEEN

IN

⬥ Relational operators for character-like data types

CO, CN, CA, NA, CS, NS, CP, NP

⬥ Relational operators for byte-like data types

BYTE-CO, BYTE-CN, BYTE-CA, BYTE-NA, BYTE-CS, BYTE-NS

⬥ Relational operators for bit patterns

O, Z, M

1. rel_exp - Relational Operators for All Data Types

rel_exp - Binary Relational Operators


operator Meaning
=, EQ Equal: True if the value of operand1 matches the value of operand2.
<>, NE  Not Equal: True if the value of operand1 does not match the value of operand2. 
<, LT  Less Than: True if the value of operand1 is less than the value of operand2. 
>, GT  Greater Than: True if the value of operand1 is greater than the value of operand2. 
<=, LE  Less Equal: True if the value of operand1 is less than or equal to the value of operand2. 
>=, GE  Greater Equal: True if the value of operand1 is greater than or equal to the value of operand2. 

The values are compared in accordance with the comparison rules.

Notes

◈ The operators =, <>, <, >, <=, and >= are equivalent to EQ, NE, LT, GT, LE, and GE respectively. It is recommended that only one of these types of operator is used within the same program. If in doubt, the operators with characters =, <, and > are considered to be more up to date, however they also overload these characters. Relational operators that consist of two letters, on the other hand, are better suited to other relational operators such as CO, CN, and so on that have no alternative forms.

◈ Due to the comparison rules, the size comparisons shown here are not suitable for determining the textual order of character-like data objects.

◈ The obsolete forms ><, =<, and => of relational operators may still appear outside of classes.

The following table shows the binary relational operators for comparisons between two operands (data objects or return values or calculation expressions) of any data types in comparison expressions.

rel_exp - Ternary Relational Operator BETWEEN

Syntax

... operand [NOT] BETWEEN operand1 AND operand2 ...

Effect

A comparison expression with the relational operator BETWEEN checks what belongs to an interval. The relational expression checks whether the content of an operand operand is within a closed interval that is delimited by the operands operand1 and operand2. The ternary comparison expression is equivalent to the following join between two binary comparison expressions:

... [NOT] ( operand >= operand1 AND operand <= operand2 ) ...

All operands are general expression positions and the usual comparison rules apply.

Example

Checks whether today is a working day and assigns the result to a variable declared inline.

DATA(work_day_flag) = xsdbool( sy-fdayw BETWEEN 1 AND 5 ).

rel_exp - Tabular Relational Operator IN

Syntax

... operand [NOT] IN seltab ...

Effect

In a comparison expression with relational operator IN, the conditions of a selection table or a table with this layout are checked, that is whether an operand operand meets the conditions of the rows in the selection table or, with addition NOT, does not meet them. The table-like comparison expression is equivalent to a join using binary or ternary comparison expressions whose number is determined by the number of rows in the internal table.

As a selection table seltab, any internal table can be specified whose row type matches that of a selection table, or a functional method with the corresponding type of return value. This includes, in particular, ranges tables. The selection table can be of any table category. For the layout of a selection table, refer to section SELECT-OPTIONS. The evaluation of a selection table requires the table to contain the valid values specified in that section in the columns sign and option. If the selection table contains invalid values, a non-handleable exception is raised. If the selection table is initial, the comparison expression is always true.

Each row in a non-initial selection table is included in the join using one of the following comparison expressions. Depending on the operator in the column option, this involves a comparison between two operands using a binary relational operator or the delimitation of an interval using the ternary operator BETWEEN.

◈ The operators "EQ", "NE", "GE", "GT", "LE", and "LT" produce a comparison of sizes:

... operand {EQ|NE|GE|GT|LE|LT} seltab-low ...

The relational operator corresponds to the content of the column seltab-option and as the right operand, the content of column seltab-low is used.

◈ The operators "CP" and "NP" produce a string comparison:

... operand {CP|NP} operand

The relational operator matches the content of the column seltab-option and the content of the columns seltab-low and seltab-high is concatenated as the right operand.

◈ The operators "BT", "NB" produce a delimitation of an interval:

... operand [NOT] BETWEEN seltab-low AND seltab-high ...

The comparison is executed without addition NOT, if the content of the column seltab-option is "BT", and with NOT, if it is "NB". For the interval boundaries, the content of the columns seltab-low and seltab-high is used.

operand is a general expression position and the usual comparison rules apply to operand and the columns low and high of the selection table. The comparison expressions of the individual rows are joined as a logical expression in accordance with the following hierarchy:

1. The expressions of all rows that contain "I" in the column sign are joined using OR. If there are no rows that contain "E" in the column sign, this represents the entire logical expression.

2. The expressions of all rows that contain "E" in the column sign are joined using OR and then negated using NOT. If there are no rows that contain "I" in the column sign, this represents the entire logical expression.

3. If the content "I" and the content "E" are both in the column sign, AND is used to join the logical expression that results from step 1 with the logical expression from step 2

Notes

◈ These rules above can be interpreted in such a way that the rows containing "I" or "E" in the column sign describe two value sets. The set for "I" is the inclusive set, the set for "E" is the exclusive set. By subtracting the exclusive set from the inclusive set, a results set is calculated that contains all values for which the entire logical expression is true.

◈ The operator IN is, due to its implementation using binary relational operators, not suitable for selecting natural-language text content.

◈ If the selection table seltab was declared using the statement SELECT-OPTIONS seltab for the data object operand, it is possible to specify the comparison expression in an obsolete short form.

Comparison with Selection Table

This example demonstrates how selection tables are evaluated in a comparison expression.

Source Code

REPORT demo_logical_expr_seltab_1 .

DATA wa_carrid TYPE spfli-carrid.

SELECT-OPTIONS airline FOR wa_carrid.

WRITE: 'Inside', 'Outside'.
SKIP.

SELECT carrid FROM spfli INTO @wa_carrid.
  IF wa_carrid IN airline.
    WRITE: / wa_carrid UNDER 'Inside'.
  ELSE.
    WRITE: / wa_carrid UNDER 'Outside'.
  ENDIF.
ENDSELECT.

Description

The SELECT loop reads all rows from the database table SPFLI and arranges them in a list, depending on their relation to the condition specified on the selection screen.

2. rel_exp - Relational Operators for Character-Like Data Types

The following table shows the relational operators for comparisons between character-like operands (single data objects or return values or string expressions) in comparison expressions. The trailing blanks are respected for operands of type string. If not stated differently in the following table, the trailing blanks are ignored for operands of types c, d, n, and t.

operator Meaning 
CO Contains Only: True, if operand1 only contains characters from operand2. It is case-sensitive and trailing blanks are respected in both operands. If operand2 is of type string and initial, the relational expression is false, except if operand1 is also of type string and initial. If operand1 has the type string and is initial, the relational expression is always true, regardless of operand2. If the comparison is true, sy-fdpos contains the offset of the first character in operand1 that is not contained in operand2. If the comparison is true, sy-fdpos contains the length of operand1. 
CN Contains Not Only: True if a relational expression with CO is false, that is, if operand1 contains not only characters from operand2. sy-fdpos is set in the same way as for CO. If the comparison is true, sy-fdpos contains the offset of the first character in operand1 that is not contained in operand2. If the comparison is false, sy-fdpos contains the length of operand1.
CA  Contains Any: True, if operand1 contains at least one character from operand2. It is case-sensitive and trailing blanks are respected in both operands. If operand1 or operand2 are of the type string and initial, the relational expression is always false. If result of the comparison is positive, sy-fdpos contains the offset of the first character in operand1 that is also contained in operand2. If the comparison is false, sy-fdpos contains the length of operand1.
NA  Contains Not Any: True, if a relational expression with CA is false, that is if operand1 does not contain any characters from operand2. If the comparison is false, sy-fdpos contains the offset of the first character in operand1 that is also contained in operand2. If the comparison is true, sy-fdpos contains the length of operand1. 
CS  Contains String: True, if the content of operand2 is contained in operand1. It is not case-sensitive and trailing blanks in the left operand are respected. If operand1 is of type string and initial, or of type c and contains only blank characters, the relational expression is false, unless operand2 is also of type string and initial, or of type c and only contains blank characters. In this case, the relational expression is always true. If the comparison is true, sy-fdpos contains the offset of operand2 in operand1. If the comparison is false, sy-fdpos contains the length of operand1. 
NS  Contains No String: True, if a relational expression with CS is false, that is if operand1 does not contain the content of operand2. If the comparison is false, sy-fdpos contains the offset of operand2 in operand1. If the comparison is true, sy-fdpos contains the length of operand1. 
CP  Covers Pattern: True, if the content of operand1 fits the pattern in operand2. Wildcard characters can be used to create the operand2 pattern, where "*" represents any character string (including a blank string) and "+" represents any character. It is not case-sensitive. Trailing blanks in the left operand are respected. If the comparison is true, sy-fdpos contains the offset of operand2 in operand1. Here, leading wildcard characters "*" in operand2 are ignored if operand2 also contains other characters. If the comparison is false, sy-fdpos contains the length of operand1. Characters in operand2 can be selected for direct comparisons by prefixing them with the escape character "#". For characters flagged in this way in operand2, the operator is case-sensitive. Also, wildcard characters and the escape character are not subject to special handling and trailing blanks are relevant. 
NP No Pattern: True, if a relational expression with CP is false, that is, if operand1 does not fit the pattern operand2. If the comparison is false, sy-fdpos contains the offset of operand2 in operand1. Here, leading wildcard characters "*" in operand2 are ignored if operand2 also contains other characters. If the comparison is true, sy-fdpos contains the length of operand1. 

Notes

◈ The operators CP and NP use multiple wildcard characters "*" in a row in the same way as a single "*" character. The wildcard character "+" does not represent a blank string.
◈ Operands of byte-like data types cannot be compared using the relational operators from this table. ◈ The relational operators for byte-like data types can be used to perform these comparisons for byte-like operands.
◈ The relational operators in this table can be replaced by predicate functions.
The statement FIND and the search functions find can be quicker than the relational operator CS by some magnitude.
◈ When using relational operators for character-like data types in a conditional expression or in a Boolean function, the system field sy-fdpos is given the value set in the expression once the expression is processed.

Example

Searches for HTML tags in a text using operator CP. This search finds the first HTML tag "<i> " at offset 8. Note that it is not enough to specify a search pattern "<*>", since CP stands for Covers Pattern, and not for Contains Pattern. The example also shows that leading wildcard characters "*" in the string are ignored in sy-fdpos so that the occurrence found by this search can be identified.

DATA html TYPE string. 

html = `This is <i>italic</i>!`. 

IF html CP '*<*>*'. 
  cl_demo_output=>display_text( |Found HTML tag at { sy-fdpos }| ). 
ENDIF.

Example

Displays the first position of a letter in a field. The operator CS is used in the conditional operator COND, which returns the content of the system field sy-fdpos.

cl_demo_output=>display( COND #( WHEN sy-abcde CS 'H' THEN sy-fdpos ) ).

Relational Operators for Character-Like Data Types

This example demonstrates the relational operators for character-like data types.

Source Code

REPORT demo_character_comparison.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    TYPES:
      BEGIN OF result,
        operand TYPE string,
        result  TYPE string,
        fdpos   TYPE sy-fdpos,
      END OF result.
    DATA results TYPE STANDARD TABLE OF result WITH EMPTY KEY.

    DATA: f1 TYPE c LENGTH 5 VALUE 'BD   ',
          f2 TYPE c LENGTH 5 VALUE 'ABCDE'.
    cl_demo_input=>new(
      )->add_field( CHANGING field = f1
      )->add_field( CHANGING field = f2 )->request( ).

    results = VALUE #( BASE results
     ( operand = 'CO'
       result     = COND #( WHEN f1 CO f2 THEN abap_true )
       fdpos      = sy-fdpos ) ).
    results = VALUE #( BASE results
     ( operand = 'CN'
       result     = COND #( WHEN f1 CN f2 THEN abap_true )
       fdpos      = sy-fdpos ) ).
    results = VALUE #( BASE results
     ( operand = 'CA'
       result     = COND #( WHEN f1 CA f2 THEN abap_true )
       fdpos      = sy-fdpos ) ).
    results = VALUE #( BASE results
     ( operand = 'NA'
       result     = COND #( WHEN f1 NA f2 THEN abap_true )
       fdpos      = sy-fdpos ) ).
    results = VALUE #( BASE results
     ( operand = 'CS'
       result     = COND #( WHEN f1 CS f2 THEN abap_true )
       fdpos      = sy-fdpos ) ).
    results = VALUE #( BASE results
     ( operand = 'NS'
       result     = COND #( WHEN f1 NS f2 THEN abap_true )
       fdpos      = sy-fdpos ) ).
    results = VALUE #( BASE results
     ( operand = 'CP'
       result     = COND #( WHEN f1 CP f2 THEN abap_true )
       fdpos      = sy-fdpos ) ).
    results = VALUE #( BASE results
     ( operand = 'NP'
       result     = COND #( WHEN f1 NP f2 THEN abap_true )
       fdpos      = sy-fdpos ) ).

    cl_demo_output=>new(
      )->write( |'{ f1 WIDTH = 5 }' operand '{ f2 WIDTH = 5 }'|
      )->display( results ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

Text fields with length 5 can be given different content and compared with each other using the available relational operators.

3. rel_exp - Relational Operators for Byte-Like Data Types

The following table shows the relational operators for comparisons between byte-like operands (single data objects or return values or bit expressions) in comparison expressions.

operator Meaning
BYTE-CO  Contains Only: True, if operand1 only contains bytes from operand2. If operand2 is of type xstring and initial, the relational expression is false, except if operand1 is also of type xstring and initial. In this case, the relational expression is always true. If the comparison is false, sy-fdpos contains the offset of the first byte in operand1, which is not contained in operand2. If the comparison is true, sy-fdpos contains the length of operand1.
BYTE-CN  Contains Not Only: True, if a relational expression with BYTE-CO is false, if therefore operand1 does not only contain bytes from operand2. If the comparison is true, sy-fdpos contains the offset of the first character in operand1 that is not contained in operand2. If the comparison is false, sy-fdpos contains the length of operand1. 
BYTE-CA  Contains Any: True, if operand1 contains at least one byte from operand2. If operand1 or operand2 are of the type xstring and initial, the relational expression is always false. If the comparison is true, sy-fdpos contains the offset of the first byte in operand1 that is also contained in operand2. If the comparison is false, sy-fdpos contains the length of operand1. 
BYTE-NA  Contains Not Any: True, if a relational expression with BYTE-CA is false, if therefore operand1 does not contain a byte from operand2. If the comparison is false, sy-fdpos contains the offset of the first byte in operand1 that is also contained in operand2. If the comparison is true, sy-fdpos contains the length of operand1.
BYTE-CS  Contains String: True, if the content of operand2 is contained in operand1. If operand1 is of type xs and initial, the relational expression is false, except if operand2 is also of type xstring and initial. In this case, the relational expression is always true. If the comparison is true, sy-fdpos contains the offset of operand2 in operand1. If the comparison is false, sy-fdpos contains the length of operand1. 
BYTE-NS  Contains No String: True, if a relational expression with BYTE-CS is false, if therefore operand1 does not contain the content of operand2. If the comparison is false, sy-fdpos contains the offset of operand2 in operand1. If the comparison is true, sy-fdpos contains the length of operand1.

Example

The logical expression in the IF statement is true, if the second half-byte is not filled for any of the bytes in hex1.

DATA: hex1 TYPE xstring, 
      hex2 TYPE xstring. 

hex1 = ... 
hex2 = '000102030405060708090A0B0C0D0E0F'. 

IF hex1 BYTE-CO hex2. 
  ... 
ENDIF.

4. rel_exp - Relational Operators for Bit Patterns

The table below shows the relational operators for comparisons of operands (single data objects or return values or bit expressions) with bit patterns in byte-like operands in comparison expressions. The data type of the right operand operand2 must be byte-like (x or xstring). It contains the bit pattern with which the left operand operand1 is compared. If operand1 is shorter than operand2, hexadecimal zeroes are added on the right of operand1 (in accordance with the comparison rules) to lengthen it appropriately. No conversion takes place. The data type of operand1 must also be byte-like (x or xstring).

operator Meaning 
O Ones: True if the bits that are 1 in operand2, are also 1 in operand1. If operand2 contains only zeroes, the relational expression is always true.
Z Zeroes: True, if the bits that are 1 in operand2 are 0 in operand1. If operand2 contains only zeroes, the relational expression is always true. 
Mixed: True, if of the bits that are 1 in operand2, at least one is 1 and one is 0 in operand1. If operand2 contains only zeroes, the relational expression is always false. 

Note

For the result of the comparisons, it is irrelevant whether the bits that are 0 in operand2, are 1 or 0 in operand1.

Example

The logical expression in the IF statement is false, because, before the comparison, 00 is added on the right of hex1. If the content of hex2 were 111100, the comparison would be true.

DATA: hex1 TYPE xstring, 
      hex2 TYPE xstring. 

hex1 = 'FFFF'. 
hex2 = '111111'. 

IF hex1 O hex2. 
  ... 
ENDIF.

◑ rel_exp - Comparison Rules

1. rel_exp - Comparing Elementary Data Types

If operand1 and operand2 in a comparison expression have elementary data types, they are used to determine a common elementary comparison type. Every comparison type has comparison rules used to perform the comparison. Operands that are not compatible with the comparison type are converted to this type. The following sections show how the comparison type is determined for the various comparisons:

◈ Comparison type of elementary data objects
◈ Comparison type of calculation expressions

The return values or results of functional methods, predefined functions, constructor expressions, and table expressions are handled like elementary data objects.

Notes

◈ Previous assignments made to helper variables with certain types or the conversion operator CONV can be used to force comparison types and conversions other than a direct comparison of the operands.
◈ It should be noted that some predefined functions operate like an arithmetic expression if they contain a numeric expression as an argument.

1.1 rel_exp - Comparison Rules for Comparison Types

When operands with elementary data types are compared, the comparison uses a comparison type defined by the operands in question, as for elementary data objects and calculation expressions. The comparison type be one of the predefined ABAP types. When incompatible operands are compared, the operands that do not have the comparison type are converted to this type.

◇ Numeric Comparison Type

If the comparison type is one of the numeric data types, the number values are compared.

Notes

1. Platform-dependent rounding errors may occur with data type f, which means it often does not make sense to compare floating point numbers to see if they match.
2. Scale and precision are not relevant in comparisons between decimal floating point numbers.

◇ Character-Like Comparison Type

If the comparison type is one of the character-like data types, the content is compared from left to right. Based on the internal binary representation in the code page used, the first differing character from the left determines which operand is greater.

Notes

◈ For operands of types c and string, the contents are not compared on the basis of the locale of the current text environment. To specify the order with reference to the locale, the statement CONVERT TEXT can be used.
◈ If operands of type n contain a valid string of digits, the proportions of the numbers represented are determined correctly.

◇ Byte-Like Comparison Type

If the comparison type is one of the byte-like data types, the content is compared from left to right. Based on the byte values, the first differing byte from the left determines which operand is greater.

◇ Date/Time Type as Comparison Type

If the comparison type is one of the date/time types, the content is compared from left to right. Based on the internal binary representation in the code page used, the first differing character from the left determines which operand is greater.

Note

For operands of types d and t containing a valid date or a valid time, the later date or time is always greater than the earlier one.

1.2 rel_exp - Comparison Type of Elementary Data Objects

The following sections describe which comparison types are produced by combining different elementary data objects. They also include the rules that determine the length in which the comparison is made when comparison types have generic lengths.

Comparison type of numeric data objects
Comparison type of character-like data objects
Comparison type of byte-like data objects
Comparison type of date fields and time fields

Note

When converting between elementary data objects with different data types, any of the exceptions listed here can be raised. In comparisons, these exceptions are either handled implicitly or produce a runtime error. They cannot, however, be handled using CATCH.

1.2.1 rel_exp - Comparison Type of Numeric Data Objects

The following tables show the comparison types for comparisons between numeric data types and other data types. If the type of an operand is not the same as the comparison type, it is converted to this type. The comparison rules for the comparison types determine how the comparison is performed.

Comparisons with Numeric Data Types
Comparisons with Character-Like Data Types
Comparisons with Byte-Like Data Types
Comparisons with Date/Time Types

Note

If a decimal floating point number is involved in a comparison, the comparison is always made with the type decfloat34.

Comparisons with Numeric Data Types

- decfloat16,
decfloat34 
int8 
decfloat16,
decfloat34 
decfloat34 decfloat34 decfloat34  decfloat34  decfloat34  decfloat34  decfloat34 
decfloat34  f
decfloat34  p
int8  decfloat34  int8 int8 int8  int8 
decfloat34  int8  i
decfloat34  int8  s
decfloat34  int8  i

Value Ranges and Length Adjustments

◈ When two operands with data type p are compared, numbers with more than 31 places are edited internally to ensure that there is no overflow.
     ◈ When two operands with data type p but with different lengths are compared, the shorter operand is converted to the length of the longer operand.
     ◈ When two operands with data type p but with different numbers of decimal places are compared, the operand with fewer decimal places is converted to a number with the same number of decimal places as the other operand.
◈ When an operand with type p is compared with an operand with type int8, i, s, or b, the comparison type p has 31 places and the number of decimal places of the operand of type p.
◈ If the value of an operand of type int8 does not match the value range of a comparison type p, the exception is caught internally and the comparison delivers the correct result.
Comparisons with Character-Like Data Types


- decfloat16,
decfloat34 
p int8  i,s,b 
string, c, n decfloat34 int8 

Value Ranges and Length Adjustments

◈ When the types string and c are compared with packed numbers of the type p, the comparison type p has 31 places together with the number of decimal places of the operand of type p. This can raise exceptions if overflows occur.
◈ When the type n is compared with packed numbers of the type p, the numeric text can contain up to 31 digits, excluding leading zeros and regardless of how many decimal places are in the operand with type p.
◈ When the types string, c, and n are compared with integers of the types int8, i, s, and b, the number value in the character-like operand does not need to fit the value range of the comparison type i. If the number value is not in the value range, the comparison produces the correct result and no exception is raised.

Comparisons with Byte-Like Data Types

-decfloat16,
decfloat34 
pint8 i,s,b 
xstring, xdecfloat34int8 i

Length Adjustments

The comparison type p has 31 places and the number of decimal places in the operand of type p.

Note

In conversions of byte-like data types to any numeric type except int8, all bytes are ignored except for the final four. In the case of int8, the final 8 bytes are respected.

Comparisons with Date/Time Types

-decfloat16,
decfloat34 
pint8 i,s,b 
d, tdecfloat34int8 i

1.2.2 rel_exp - Comparison Type of Character-Like Data Objects

The following tables show the comparison types for comparisons between character-like data types and other data types. If the type of an operand is not the same as the comparison type, it is converted to this type. The comparison rules for the comparison types determine how the comparison is performed. If no comparison type is specified for a combination, no comparison is possible.

Comparisons with Numeric Data Types

string, c, n 
decfloat16, decfloat34 decfloat34 
f f
int8 int8 
i,s,b 

Value Ranges and Length Adjustments

◈ When the types string and c are compared with packed numbers of the type p, the comparison type p has 31 places together with the number of decimal places of the operand of type p. This can raise exceptions if overflows occur.

◈ When the type n is compared with packed numbers of the type p, the numeric text can contain up to 31 digits, excluding leading zeros and regardless of how many decimal places are in the operand with type p.

◈ When the types string, c, and n are compared with integers of the types int8, i, s, and b, the number value in the character-like operand does not need to fit the value range of the comparison type i. If the number value is not in the value range, the comparison produces the correct result and no exception is raised.

Comparisons with Character-Like Data Types

- string 
string string  string 
c String 
n

Length Adjustments

◈ Operands with a different length of data type string never match. If the contents of the operands match across the length of the shorter operand, the shorter operand is smaller than the longer one. Otherwise the surplus places in the longer field are cut off on the right, and then the content is compared.

◈ For comparisons between two operands of data type c with the same length, the entire length is compared, which means the trailing blanks are taken into account. For comparisons between two operands of data type c with different lengths, the shorter field is converted to the longer field, with blanks used as padding on the right.

◈ For comparisons between two operands of data type n, the shorter field is converted to the longer field, with the character "0" used as padding on the left.

Note

For comparisons between text fields of the type c and text strings of the type string, note that trailing blanks are ignored by conversions from c to string. This can have unexpected results. The conversion operator CONV can be used here to force other comparison types (see the example after the link).

Example

In the following comparison, the comparison type is c and the comparison takes places in the current code page, in accordance with the binary representation. In most code pages, "a" is greater than "Z". See also the example for CONVERT TEXT.

IF 'a' > 'Z'. 
  cl_demo_output=>display_text( `'a' > 'Z'` ). 
ELSE. 
  cl_demo_output=>display_text( `'a' < 'Z'` ). 
ENDIF.

Example

The following comparison is false, which is probably unexpected. The value returned by boolc has the type string and includes a blank, whereas the constant abap_false has the type c. For the comparison, the value of abap_false is converted to an empty string, since the blank it contains is ignored.

IF boolc( 1 = 2 ) = abap_false. 
  cl_demo_output=>display_text( 'yes' ). 
ELSE. 
  cl_demo_output=>display_text( 'no' ). 
ENDIF.

The following comparison, however, is true, since the return value of xsdbool has the same ABAP type as the constant abap_false.

IF xsdbool( 1 = 2 ) = abap_false. 
  cl_demo_output=>display_text( 'yes' ). 
ELSE. 
  cl_demo_output=>display_text( 'no' ). 
ENDIF.

Comparisons with Byte-Like Data Types

- string 
xstring string  string 
x String 

Length Adjustments

◈ When data type c is compared with x or xstring, the shorter field is adjusted to the length of the longer field after conversions from x to c or xstring to string. Blanks are used as filler on the right.

◈ Lengths are not adjusted for comparisons between the data type string and x or xstring.

Comparisons with Date/Time Types

- string 
d,t string  c n

Length Adjustments

◈ For comparisons between data types c, n, or string on the one hand and d on the other, the longer field is truncated on the right to the length of the shorter field, as long as only blanks are cut off.

◈ For comparisons between data types c or n on the one hand and t on the other, the longer field is truncated on the right to the length of the shorter field, as long as only blanks are cut off.

◈ Lengths are not adjusted for comparisons between the data type string and the data type t.

1.2.3 rel_exp - Comparison Type of Byte-Like Data Objects

The following tables show the comparison types for comparisons between byte-like data types and other data types. If the type of an operand is not the same as the comparison type, it is converted to this type. The comparison rules for the comparison types determine how the comparison is performed.

Comparisons with Numeric Data Types

 - xstring, x
decfloat16, decfloat34  decfloat34 
int8  int8 
i, s, b 

Length Adjustments

The comparison type p has 31 places and the number of decimal places in the operand of type p.

Note

In conversions of byte-like data types to any numeric type except int8, all bytes are ignored except for the final four. In the case of int8, the final 8 bytes are respected.

Comparisons with Character-Like Data Types

 - xstring 
string string string 
string 
n p p

Length Adjustments

◈ When data type c is compared with x or xstring, the shorter field is adjusted to the length of the longer field after conversions from x to c or xstring to string. Blanks are used as filler on the right.
◈ Lengths are not adjusted for comparisons between the data type string and x or xstring.

Comparisons with Byte-Like Data Types

- xstring 
xstring xstring xstring
x xstring

Length Adjustments

◈ Operands of data type xstring with different lengths never match. If the contents of the operands match across the length of the shorter operand, the shorter operand is smaller than the longer one. Otherwise the surplus bytes in the longer field are cut off on the right, and then the content is compared.
◈ For comparisons between two operands of data type c, the shorter field is converted to the longer field, with hexadecimal 0 used as padding on the right.

Example

The first comparison uses the appropriate conversion rules to convert the hexadecimal content "FF00" of hex to the string "FF00"; this string is then compared with "FFxx". Before the second comparison, the appropriate conversion rules are used to convert the content of text to the hexadecimal value "FF00" in the helper variable hex_helper and this value is compared with the content of hex.

DATA hex        TYPE x LENGTH 2. 
DATA text       TYPE c LENGTH 4. 
DATA hex_helper TYPE x LENGTH 2. 

hex  = 'FF00'. 
text = 'FFxx'. 

IF hex <> text. 
  cl_demo_output=>write_text( |{ hex } <> { text } | ). 
ENDIF. 

hex_helper = text. 
IF hex = hex_helper. 
  cl_demo_output=>write_text( |{ hex } = { hex_helper } | ). 
ENDIF. 

cl_demo_output=>display( ).

Comparisons with Date/Time Types

- xstring, x 
d, t

1.2.4 rel_exp - Comparison Type of Date Fields and Time Fields

The following tables show the comparison types for comparisons between date/time types and other data types. If the type of an operand is not the same as the comparison type, it is converted to this type. The comparison rules for the comparison types determine how the comparison is performed. If no comparison type is specified for a combination, no comparison is possible.

Comparisons with Numeric Data Types

 - d,t
decfloat16, decfloat34  decfloat34
f
int8  int8 
i, s, b 

Comparisons with Character-Like Data Types

 - d,t
string string
c c

Length Adjustments

◈ For comparisons between data types c, n, or string on the one hand and d on the other, the longer field is truncated on the right to the length of the shorter field, as long as only blanks are cut off.

◈ For comparisons between data types c or n on the one hand and t on the other, the longer field is truncated on the right to the length of the shorter field, as long as only blanks are cut off.

◈ Lengths are not adjusted for comparisons between the data type string and the data type t.

Comparisons with Byte-Like Data Types

 - d,t
xstring, x i

Comparisons with Date/Time Types

-

1.3 rel_exp - Comparison Type of Calculation Expressions

Any calculation expression can be specified as an operand of a comparison expression.

The table below lists all combinations that are possible for calculation expressions in comparison expressions:

One Page Relational Operator Other Page
Single operand with numeric data type or arithmetic expression  =, EQ, <>, NE, <, LT, >, GT, <=, LE, >=, GE  Arithmetic expression 
Single operand with any elementary data type or string expression  =, EQ, <>, NE, <, LT, >, GT, <=, LE, >=, GE, CO, CN, CA, NA, CS, NS, CP, NP  String expression 
Single operand with byte-like data type or bit expression =, EQ, <>, NE, <, LT, >, GT, <=, LE, >=, GE, BYTE-CO, BYTE-CN, BYTE-CA, BYTE-NA, BYTE-CS, BYTE-NS, O, Z, M  Bit expression 

The comparison type is determined differently depending on the type of calculation expression.

◉ Comparison type of arithmetic expressions
◉ Comparison type of string expressions
◉ Comparison type of bit expressions

In the case of comparison expressions using the relational operators BETWEEN and IN that are switched internally to joins of comparisons with the binary operators above, the rules of the individual comparisons apply.

Notes

◉ Relational expressions cannot yet be compared. Instead, they can be joined using Boolean operators, where the join with the operator EQUIV corresponds to the equality comparison of the resulting truth values.
◉ A calculation expression cannot be specified as the operand of a predicate expression.

1.3.1 rel_exp - Comparison Type of Arithmetic Expressions

Arithmetic expressions can be used as operands of comparison expressions using relational operators for all data types. It is possible to compare an arithmetic expression with a single operand with a numeric data type or with another arithmetic expression.

The calculation type of all arithmetic expressions in a comparison expression is determined by all single operands of the entire comparison expression as well as by any operators **, using the usual rules. The result of the arithmetic expressions involved exists in the calculation type. This calculation type is also the comparison type whose comparison rules are used to make the comparison. If necessary, single operands are converted to the comparison type before the comparison starts.

If multiple relational expressions are joined as a logical expression using Boolean operators, the calculation type or relational type are determined separately for each relational expression.

Notes

◈ If a conversion error occurs in an arithmetic expression in a relational expression, the corresponding exception can be handled (unlike in direct comparisons of data objects.
◈ To compare an arithmetic expression with a single non-numeric operand, the operand can be prefixed with the "+" sign to turn it into an arithmetic expression.
◈ An arithmetic expression cannot be specified as the operand of a predicate expression.

1.3.2 rel_exp - Comparison Type of String Expressions

String expressions can be used as operands in comparison expressions with

◈ relational operators for all data types
◈ relational operators for character-like data types

It is possible to compare a string expression with a single operand with any elementary data type or with another string expression.

If one of the operands of a comparison is a string expression, the comparison type whose comparison rules are used to perform the comparison is always string. If necessary, single operands are converted to the type string before the comparison.

Note

A string expression cannot be specified as the operand of a predicate expression.

Example

This example demonstrates the effect of different comparison types. The first comparison is true, since the character-like operand is converted to the type of the numeric operand (in accordance with the rule for comparing character-like data types) and the number value is compared. The second comparison is false, since the numeric operand is converted to the type string of the string expression and the internal representation of the code page used is compared.

IF `02` > 1. 
  WRITE / 'yes'. 
ELSE. 
  WRITE / 'no'. 
ENDIF. 

IF |02| > 1. 
  WRITE / 'yes'. 
ELSE. 
  WRITE / 'no'. 
ENDIF.

1.3.3 rel_exp - Comparison Type of Bit Expressions

Bit expressions can be used as operands in comparison expressions with

◈ relational operators for all data types
◈ relational operators for byte-like data types
◈ relational operators for bit patterns

A bit expression can be compared with a single operand of a byte-like data type or with another bit expression.

Each bit expression of a comparison expression is calculated based on the length of its longest operand, with shorter operands padded on the right with hexadecimal 0. All operands of the entire comparison expression are respected. The result of the bit expressions involved is contained within this length and, if necessary, a single operand is expanded to this length before the comparison, again by padding with hexadecimal 0 on the right. The comparison is then performed using the comparison rule for a byte-like comparison type.

Note

A bit expression cannot be specified as the operand of a predicate expression.

Example

This example demonstrates the different ways lengths are handled in comparisons between byte fields and bit expressions. The bit expression in the first comparison is evaluated with length 4 and produces the value hexadecimal 1100. The single operand on the right is expanded to the value hexadecimal 1100 by padding it with hexadecimal 00 and the equality comparison is true. In the second comparison, on the other hand, the operand on the left is converted to the type xstring and the operand on the right is not made longer. Here, the inequality comparison is true.

IF x`FFFF` BIT-AND x`11` = xstring`11`. 
  WRITE / 'yes'. 
ENDIF. 

IF x`1100` <> xstring`11`. 
  WRITE / 'no'. 
ENDIF.

2. rel_exp - Comparing Reference Variables

It is possible to compare data references to data references, and object references to object references, but not data references to object references. Two reference variables are identical if they point to the same object. A comparison of size is defined internally and always delivers the same results in similar situations.

Note

For data references to be identical it is not enough for the operands to contain the same reference. Instead, the data type of the objects references must be compatible. If, for example, two reference variables have the same memory address, and one points to a structure, the other to the first component of the structure, then these variables are not identical since the data type of the operands is incompatible. Reference variables filled using GET REFERENCE might not be identical, even though they point to the same data object, if GET REFERENCE or the reference operator REF is executed for a field symbol to which the data object was assigned using casting.

3. rel_exp - Comparison of Structures

It is possible to compare structures to structures and to elementary fields.

◈ Compatible structures are compared on a component-by-component basis and any nested structures are resolved recursively. Two structures are equal if the contents of their components are equal. If two structures are not equal, the first unequal component pair determines the result of the comparison.

◈ It is possible to compare incompatible structures and structures with elementary fields provided that the structures concerned are flat.

     ◈ Comparisons between two flat structures require that their fragment views match for the length of the shorter structure. Before the comparison, the shorter structure is padded to the length of the longer structure. Here, all character-like components are padded with blanks and all other components with a type-friendly initial value. The comparison is then performed on a section-by-section basis according to the fragment view.

     ◈ Comparisons between a flat structure and an elementary field are subject to the following rules:

If the flat structure is character-like, it is handled like an elementary field with the type c in the comparison.

If the flat structure is not just character-like, the elementary field must have the type c and the first fragment of the structure fragment view must be character-like and at least as long as the elementary field. If the elementary field is shorter than the structure, it is expanded to the length of the structure before the comparison is started and then handled implicitly like a structure. The character-like parts of the extension are padded with blanks and all other components are padded with the type-friendly initial value.

4. rel_exp - Comparing Internal Tables

It is possible to compare internal tables with other internal tables if their row types are can be compared. Internal tables are compared based on the following hierarchy:

1. The internal table that has more rows than the other internal table is the larger table.

2. Internal tables with the same number of rows are compared row by row. If an internal table contains nested internal tables, these are compared recursively. Two internal tables are identical if the contents of each single row match. If two internal tables are not identical, the first non-matching pair of rows determines the result of the comparison.

Note

If an internal table with a header line is specified as an operand of an assignment, the header line is addressed in nearly all operand positions and not the table body. To address the table body of a table with a header, [] must be appended to the name.

4.1 Comparing Internal Tables

This example demonstrates how internal tables are compared.

Source Code

REPORT demo_int_tables_compare .

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.

    TYPES: BEGIN OF line,
             col1 TYPE i,
             col2 TYPE i,
           END OF line.

    DATA: itab TYPE TABLE OF line WITH EMPTY KEY,
          jtab TYPE TABLE OF line WITH EMPTY KEY.

    DATA(out) = cl_demo_output=>new( ).

    itab = VALUE #( FOR j = 1 UNTIL j > 3
      ( col1 = j col2 = j ** 2 ) ).

    jtab = itab.

    itab = VALUE #( BASE itab
                    ( col1 = 10 col2 = 20 ) ).

    IF itab > jtab.
      out->write( 'ITAB >  JTAB' ).
    ENDIF.

    jtab = VALUE #( BASE jtab
                    ( col1 = 10 col2 = 20 ) ).

    IF itab = jtab.
      out->write( 'ITAB =  JTAB' ).
    ENDIF.

    itab = VALUE #( BASE itab
                    ( col1 = 30 col2 = 80 ) ).

    IF jtab <= itab.
      out->write( 'JTAB <= ITAB' ).
    ENDIF.

    jtab = VALUE #( BASE jtab
                    ( col1 = 50 col2 = 60 ) ).

    IF itab <> jtab.
      out->write( 'ITAB <> JTAB' ).
    ENDIF.

    IF itab < jtab.
      out->write( 'ITAB <  JTAB' ).
    ENDIF.

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

Two standard tables, itab and jtab, are created. itab is filled with three rows and assigned to jtab. A further row is added to itab and the first relational expression returns the result that itab is greater than jtab. After the same row has been added to jtab, the second relational expression returns the result that both tables are the same. Another row is added to itab and the third relational expression returns the result that jtab is less than or equal to itab. Next, a further row is added to jtab, the content of which is different from the final row of itab. The next relational expression returns the result that itab is not equal to jtab. The first table field where the contents of itab and jtab differ is col1 in the last row of the table, namely 30 for itab and 50 for jtab. In the last relational expression, itab is therefore less than jtab.

5. rel_exp - Comparing Meshes

Meshes can only be compared with meshes that are fully compatible. The comparison is performed component by component, as for the corresponding structure. The rules for internal tables also apply when comparing components.

◉ rel_exp - Predicates

Predicates qualify an operand.

1. Predicate Expressions

syntax

...   {operand  IS [NOT] INITIAL}
    | {ref      IS [NOT] BOUND}
    | {oref     IS [NOT] INSTANCE OF}
    | {<fs>     IS [NOT] ASSIGNED}
    | {para     IS [NOT] SUPPLIED} ...

Effect

A predicate expression uses the predicate operator IS to produce a truth value from the state of an operand.

Notes

◈ With the exception of INSTANCE OF, no operands of the possible predicate expressions are general expression positions. No predicate expression can be applied to a calculation expression.
◈ The predicate expression IS [NOT] REQUESTED is obsolete and was replaced by IS [NOT] SUPPLIED.

🔷 rel_exp - IS INITIAL

Syntax

... operand IS [NOT] INITIAL ...

Effect

This predicate expression checks whether the operand operand is initial. The expression is true, if the operand contains its type-friendly initial value. Any data objects can be specified for operand. This is an extended functional operand position in which, alongside functional method calls, constructor expressions, or table expressions, certain predefined functions can also be specified.

With the addition NOT, the expression is true if the operand contains a value other than its type-friendly initial value

Notes

◈ If a functional method call is specified as operand,
... operand IS NOT INITIAL ...
can have a predicative method call as a short form.

     ◈ It is possible, but not recommended, to always specify predefined functions or expressions as operand, for example:
     ◈ It is better to use str IS INITIAL or itab IS INITIAL instead of strlen( str ) IS INITIAL or lines( itab ) IS INITIAL.
     ◈ It is better to use the predicate function matches instead of match( ... ) IS INITIAL.
◈ It is better to use the arguments of the constructor expression instead of VALUE type( ... ) IS INITIAL.

◈ Calculation expressions cannot be specified for operand.

Example

The logical expression in the IF statement is true if the internal table in the SELECT statement was filled with rows.

DATA spfli_tab TYPE TABLE OF spfli.
...
CLEAR spfli_tab.
SELECT *
       FROM spfli
       WHERE ...
       INTO TABLE @spfli_tab.

IF spfli_tab IS NOT INITIAL.
  ...
ENDIF.

🔷 rel_exp - IS BOUND

Syntax

... ref IS [NOT] BOUND ...

Effect

This predicate expression checks whether a reference variable contains a valid reference. A reference variable must be specified for ref. This is a functional operand position.

◈ A data reference is valid if it can be dereferenced.
◈ An object reference is valid if it points to an object.

When using addition NOT, the expression is true if the reference variable does not contain a valid reference.

Note

A non-initial reference variable that contains a heap reference is generally always valid (since it keeps an object alive). Only heap references that point to rows from internal tables can become invalid when rows are deleted. A data reference variable that contains a stack reference, on the other hand, can become invalid even if the reference data object is removed from the stack.

Example

The logical expression in the IF statement is false. The data reference dref contains a reference to a deleted table row.

DATA: dref TYPE REF TO data,
      itab TYPE TABLE OF ...

FIELD-SYMBOLS <fs> TYPE ANY.

dref = REF #( itab[ ... ] ).

...

CLEAR itab.

...

IF dref IS BOUND.
  ASSIGN dref->* TO <fs>.
ENDIF.

🔷 rel_exp - IS INSTANCE OF

Syntax

... oref IS [NOT] INSTANCE OF class|intf

Effect

The predicate expression IS INSTANCE OF checks whether

◈ the dynamic type is more specific or equal to a comparison type for a non-initial object reference variable oref.
◈ the static type is more specific or equal to a comparison type for an initial object reference variable oref.

The comparison type must be an object type, meaning a class specified using class or an interface specified using intf that can be used in this place. oref expects an object reference variable with the static type of a class or of an interface. oref is a general expression position.

The expression is true in the following cases or false if NOT is used:

◈ The object reference variable oref is not initial and points to an object whose class
     ◈ is either the class specified using class or one of its subclasses
     ◈ or implements the interfaces specified using intf.
◈ The object reference variable oref is initial and the static type of oref is
     ◈ either the same class of subclass of class
     ◈ or contains the interface intf as a component.

If a field symbol or a formal parameter is specified for oref, its type can be fully generic, which means it can be typed with any and it must be an object reference variable at runtime. If a generically typed field symbol or a generically typed formal parameter is specified for oref, the static type of the object reference variable is used that is represented by the field symbol or formal parameter at runtime.

A syntax error occurs if it is possible to identify statically that the result of the expression is false.

Notes

◈ The predicate expression IS INSTANCE OF checks whether a cast from oref to an object reference variable with the static type class or intf is possible.
     ◈ If IS INSTANCE OF is true for a non-initial object reference variable oref, a down cast is possible. This means that the predicate expression IS INSTANCE OF can be used as condition for a down cast instead of the associated exception handler.
     ◈ If a predicate expression IS INSTANCE OF is true for an initial object reference variable oref, an up cast is possible. Checks of this type are generally suitable for generically typed field symbols or formal parameters. In this way, it is possible to detect at runtime whether the field symbol or the formal parameter represents an object reference variable of a specific static type.
◈ If the static type of oref is an interface and a class class is specified as the comparison type, the result of the expression is always false if the reference variable is initial.
◈ The special case distinction CASE TYPE OF makes it possible to abbreviate consecutive predicate expressions IS INSTANCE OF.

Example

All the following predicate expressions are true, since the dynamic type of the object reference variable oref is more specific or equal to the specified object type. See also Predicate Expression IS INSTANCE OF for a detailed example.

INTERFACE intf.
ENDINTERFACE.

CLASS c1 DEFINITION.
  PUBLIC SECTION.
    INTERFACES intf.
ENDCLASS.

CLASS c2 DEFINITION INHERITING FROM c1.
ENDCLASS.

DATA oref TYPE REF TO object.

oref = NEW c1( ).
ASSERT oref IS INSTANCE OF c1.
ASSERT oref IS INSTANCE OF intf.

oref = NEW c2( ).
ASSERT oref IS INSTANCE OF c2.
ASSERT oref IS INSTANCE OF c1.
ASSERT oref IS INSTANCE OF intf.

Example

The following example shows how the predicate expression IS INSTANCE OF can be used to check the feasibility of a down cast. This makes the exception handler (also shown) superfluous.

CLASS c1 DEFINITION.
ENDCLASS.

CLASS c2 DEFINITION INHERITING FROM c1.
ENDCLASS.

DATA: ref1 TYPE REF TO c1,
      ref2 TYPE REF TO c2.

ref1 = NEW c2( ).

IF ref1 IS INSTANCE OF c2.
  ref2 ?= ref1.
ENDIF.

TRY.
    ref2 ?= ref1.
  CATCH cx_sy_move_cast_error.
ENDTRY.

Predicate Expression IS INSTANCE OF

This example demonstrates the predicate expression IS INSTANCE OF.

Source Code

REPORT demo_is_instance_of.

INTERFACE intf.
ENDINTERFACE.

CLASS c1 DEFINITION.
  PUBLIC SECTION.
    INTERFACES intf.
ENDCLASS.

CLASS c2 DEFINITION INHERITING FROM c1.
ENDCLASS.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA:
      oref  TYPE REF TO object,
      iref  TYPE REF TO intf,
      c1ref TYPE REF TO c1,
      c2ref TYPE REF TO c2.

    FIELD-SYMBOLS <fs> TYPE any.

    DATA(out) = cl_demo_output=>new( ).

    out->next_section( `Reference Variable Not Initial`).

    out->begin_section( `TYPE REF TO object`).
    oref = NEW c1( ).
    ASSERT oref IS NOT INSTANCE OF c2.
    out->write( `oref pointing to c1 is not instance of c2` ).
    ASSERT oref IS INSTANCE OF c1.
    out->write( `oref pointing to c1 is instance of c1` ).
    ASSERT oref IS INSTANCE OF intf.
    out->write( `oref pointing to c1 is instance of intf` ).
    oref = NEW c2( ).
    ASSERT oref IS INSTANCE OF c2.
    out->write( `oref pointing to c2 is instance of c2` ).
    ASSERT oref IS INSTANCE OF c1.
    out->write( `oref pointing to c2 is instance of c1` ).
    ASSERT oref IS INSTANCE OF intf.
    out->write( `oref pointing to c2 is instance of intf` ).
    out->end_section( ).

    out->begin_section( `TYPE REF TO intf`).
    iref = NEW c1( ).
    ASSERT iref IS NOT INSTANCE OF c2.
    out->write( `iref pointing to c1 is not instance of c2` ).
    ASSERT iref IS INSTANCE OF c1.
    out->write( `iref pointing to c1 is instance of c1` ).
    ASSERT iref IS INSTANCE OF intf.
    out->write( `iref pointing to c1 is instance of intf` ).
    iref = NEW c2( ).
    ASSERT iref IS INSTANCE OF c2.
    out->write( `iref pointing to c2 is instance of c2` ).
    ASSERT iref IS INSTANCE OF c1.
    out->write( `iref pointing to c2 is instance of c1` ).
    ASSERT iref IS INSTANCE OF intf.
    out->write( `iref pointing to c2 is instance of intf` ).
    out->end_section( ).

    out->begin_section( `TYPE REF TO c1`).
    c1ref = NEW c1( ).
    ASSERT c1ref IS NOT INSTANCE OF c2.
    out->write( `c1ref pointing to c1 is not instance of c2` ).
    ASSERT c1ref IS INSTANCE OF c1.
    out->write( `c1ref pointing to c1 is instance of c1` ).
    ASSERT c1ref IS INSTANCE OF intf.
    out->write( `c1ref pointing to c1 is instance of intf` ).
    c1ref = NEW c2( ).
    ASSERT c1ref IS INSTANCE OF c2.
    out->write( `c1ref pointing to c2 is instance of c2` ).
    ASSERT c1ref IS INSTANCE OF c1.
    out->write( `c1ref pointing to c2 is instance of c1` ).
    ASSERT c1ref IS INSTANCE OF intf.
    out->write( `c1ref pointing to c2 is instance of intf` ).
    out->end_section( ).

    out->begin_section( `TYPE REF TO c2`).
    c2ref = NEW c2( ).
    ASSERT c2ref IS INSTANCE OF c2.
    out->write( `c2ref pointing to c2 is instance of c2` ).
    ASSERT c2ref IS INSTANCE OF c1.
    out->write( `c2ref pointing to c2 is instance of c1` ).
    ASSERT c2ref IS INSTANCE OF intf.
    out->write( `c2ref pointing to c2 is instance of intf` ).
    out->end_section( ).

    out->next_section( `Reference Variable Initial`).

    out->begin_section( `TYPE REF TO object`).
    ASSIGN oref TO <fs>.
    CLEAR <fs>.
    ASSERT <fs> IS NOT INSTANCE OF c1.
    out->write( `oref pointing to nothing is not instance of c1` ).
    ASSERT <fs> IS NOT INSTANCE OF c2.
    out->write( `oref pointing to nothing is not instance of c2` ).
    ASSERT <fs> IS NOT INSTANCE OF intf.
    out->write( `oref pointing to nothing is not instance of intf` ).
    out->end_section( ).

    out->begin_section( `TYPE REF TO intf`).
    ASSIGN iref TO <fs>.
    CLEAR <fs>.
    ASSERT <fs> IS NOT INSTANCE OF c1.
    out->write( `iref pointing to nothing is not instance of c1` ).
    ASSERT <fs> IS NOT INSTANCE OF c2.
    out->write( `iref pointing to nothing is not instance of c2` ).
    ASSERT <fs> IS INSTANCE OF intf.
    out->write( `iref pointing to nothing is instance of intf` ).
    out->end_section( ).

    out->begin_section( `TYPE REF TO c1`).
    ASSIGN c1ref TO <fs>.
    CLEAR <fs>.
    ASSERT <fs> IS INSTANCE OF c1.
    out->write( `c1ref pointing to nothing is instance of c1` ).
    ASSERT <fs> IS NOT INSTANCE OF c2.
    out->write( `c1ref pointing to nothing is not instance of c2` ).
    ASSERT <fs> IS INSTANCE OF intf.
    out->write( `c1ref pointing to nothing is instance of intf` ).
    out->end_section( ).

    out->begin_section( `TYPE REF TO c2`).
    ASSIGN c2ref TO <fs>.
    CLEAR <fs>.
    ASSERT <fs> IS INSTANCE OF c1.
    out->write( `c2ref pointing to nothing is instance of c1` ).
    ASSERT <fs> IS INSTANCE OF c2.
    out->write( `c2ref pointing to nothing is instance of c2` ).
    ASSERT <fs> IS INSTANCE OF intf.
    out->write( `c2ref pointing to nothing is instance of intf` ).
    out->end_section( ).

    out->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

A class c1 contains the interface intf and is the superclass of a class c2. For reference variables of the static types object, intf, c1, and c2, this example demonstrates the behavior of the predicate expression IS INSTANCE OF if these variables point to instances of c1 or c2 or are initial.

If the object reference variable is non-initial, the predicate expression is always true if a down cast to the comparison type is possible. If the object reference variable is initial, the predicate expression is true only if an up cast is possible. This example checks a generically typed field symbol for which this can only be determined at runtime.

For an initial reference variable with the static type object, the result of the expression for all comparison types except for object is false. For an initial reference variable with the static type intf, the result of the expression for all comparison types except for the interface intf is itself false.

🔷 rel_exp - IS ASSIGNED

Syntax

... <fs> IS [NOT] ASSIGNED ...

Effect

This predicate expression checks whether a memory area is assigned to a field symbol <fs>. The expression is true if the field symbol points to a memory area. <fs> expects a field symbol declared using FIELD-SYMBOLS or declared inline using FIELD-SYMBOL( ).

If the addition NOT is used, the expression is true if no memory area is assigned to the field symbol.

Example

Assigns a data object to a field symbol if no memory area has been assigned yet.

FIELD-SYMBOLS <fs> TYPE c.

...

IF <fs> IS NOT ASSIGNED.
  ASSIGN 'Standard Text' TO <fs>.
ENDIF.

...

🔷 rel_exp - IS SUPPLIED

Syntax

... para IS [NOT] SUPPLIED ...

Effect

This predicate expression checks whether a formal parameter para of a procedure is filled or requested. The expression is true if at the call an actual parameter was assigned to the formal parameter.

This relational expression can be used only in function modules and methods. For para, all optional formal parameters can be specified.

With addition NOT, the expression is true if at the call no actual parameter was assigned to the formal parameter.

For function modules called with Remote Function Call, the application servers of the calling and called program must have at least release 4.6.

Notes

◈ The predicate expression IS SUPPLIED is not evaluated in function modules that are called using
     ◈ CALL FUNCTION ... IN UPDATE TASK ...
     ◈ CALL FUNCTION ... STARTING NEW TASK ...
     ◈ from an external RFC interface.

In these cases, IS SUPPLIED always returns the value true.

◈ The predicate expression IS SUPPLIED includes the obsolete expression IS REQUESTED.

Example

The logical expression of the first IF statement in method m1 is true if at the call, an actual parameter is assigned to formal parameter p1. A check for the initial value would not be sufficient in this context, because this is the value of the replacement parameter specified with DEFAULT. The logical expression of the second IF statement is true if at the call no actual parameter is assigned to formal parameter p2.

CLASS c1 DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS m1 IMPORTING p1 TYPE i DEFAULT 0
                     EXPORTING p2 TYPE i.
ENDCLASS.

CLASS c1 IMPLEMENTATION.
  METHOD m1.
    IF p1 IS SUPPLIED.
      ...
    ELSE.
      ...
    ENDIF.
    IF p2 IS NOT SUPPLIED.
      RETURN.
    ELSE.
      ...
    ENDIF.
  ENDMETHOD.
ENDCLASS.

2. Predicate Functions

The return value of a predicate function is a logical value. A predicate function is a special formulation of a relational expression and, like any relational expression, can be a full logical expression or part of a logical expression. This means it can be specified as a condition in control statements and other statements, as an argument in Boolean functions or conditional expressions, or in joins with Boolean operators.

2.1 Predicate Functions for Character-Like Arguments

The following predicate functions exist for character-like arguments;

contains, contains_...
matches

- - rel_exp - contains , contains_..., Predicate

Syntax Forms

1. ... contains( val = text  sub|start|end = substring [ case = case]
                [ off = off] [ len = len] [occ = occ] ) ...

2. ... contains( val = text regex = regex [case = case]
                [ off = off] [ len = len] [occ = occ] ) ...

3. ... contains_any_of( val = text sub|start|end = substring
                       [ off = off] [ len = len] [occ = occ] ) ...

4. ... contains_any_not_of( val = text sub|start|end = substring
                           [ off = off] [ len = len] [occ = occ] ) ...

Effect

These predicate functions provide a truth value for a condition of the argument text.

◈ The variants of the function contains with the parameters sub, start, or end scan a search range defined by off and len in text for matches with the string specified in substring. The return code is true if at least number of occurrences specified in occ is found. The search is case-sensitive by default, but this can be overridden using the parameter case. If substring is passed to start or end, the matches must occur directly after each other either at the start or at the end of the search range; sub allows the matches to occur anywhere in the search range. If substring is empty, an exception from the class CX_SY_STRG_PAR_VAL is raised.

◈ The variants of the function contains with the parameter regex scan a search range defined by off and len in text for matches with the regular expression specified in regex. The return code is true if at least number of occurrences specified in occ is found. The search is case-sensitive by default, but this can be overridden using the parameter case.

◈ The function contains_any_of has the same effect as contains, but does not check for the occurrences of the entire string in substring; instead it checks for the individual characters in substring. This check is always case-sensitive. The return code is true if text contains at least the set of characters specified in occ. If start or end are specified, the characters can be in any order at the start or at the end of the search range. sub allows them to be anywhere.
◈ The function contains_any_not_of has the same effect as contains_any_of, but does not require the characters from substring; instead it requires any characters that are not in substring.

occ is a numeric expression position of type i and its default value is 1. Specifying a value less than or equal to 0 raises an exception of the class CX_SY_STRG_PAR_VAL.

Notes

◈ The parameter occ has a different meaning here than in other functions used for searching.
◈ The functions shown here can replace the relational operators for character-like data types as follows:

‏Relational Operator Predicate Function 
o1 CO o2  NOT contains_any_not_of( val = o1 sub = o2 )
o1 CN o2  contains_any_not_of( val = o1 sub = o2 ) 
o1 CA o2  contains_any_of( val = o1 sub = o2 ) 
o1 NA o2  NOT contains_any_of( val = o1 sub = o2 ) 
o1 CS o2  contains( val = to_upper( o1 ) sub = to_upper( o2 ) ) 
o1 NS o2  NOT contains( val = to_upper( o1 ) sub = to_upper( o2 ) ) 

Since the relational operators CS and NS are not case-sensitive, the arguments must be processed by the functions to_upper (or to_lower). It should also be noted that the trailing blanks of character-like arguments of fixed length are always ignored in the predicate functions, whereas there are exceptions to this rule for operands of relational operators.

The operators CP and NP can be replaced by the predicate function contains and a suitable regular expression. The differences in case handling must also be respected here. Generally, a simple mapping like in CS and NS is not possible, but is also not usually required.

Example

The IF block is reached in the following code excerpt since neither the first nor the last character in html occur in the system field sy-abcde.

DATA html TYPE string. 

html = '<body>Text</body>'. 

IF contains_any_not_of( val = to_upper( html ) start = sy-abcde ) AND 
   contains_any_not_of( val = to_upper( html ) end   = sy-abcde ). 
  ... 
ENDIF. 

Exceptions

Handleable Exceptions

CX_SY_RANGE_OUT_OF_BOUNDS

◈ Cause: Illegal offset or length specification in the off and len. 
Runtime Error: STRING_OFFSET_TOO_LARGE 

CX_SY_REGEX_TOO_COMPLEX

◈ Cause: More information: Exceptions in Regular Expressions. 
Runtime Error: REGEX_TOO_COMPLEX 

CX_SY_STRG_PAR_VAL

◈ Cause: Substring in substring or regular expression in regex is empty or occurrence in occ is less than or equal to 0. 
Runtime Error: STRG_ILLEGAL_PAR 

- - rel_exp - matches, Predicate Function

Syntax

... matches( val = text regex = regex [case = case]
             [off = off] [len = len] ) ...

Effect

The predicate function matches compares a search range of the argument text, defined using off and len, with the regular expression specified in regex. A corresponding truth value is returned. The return value is true when the whole search range matches the regular expression. The comparison is case-sensitive by default, but this can be overridden using the parameter case.

Note

The match function match can be used to return a substring that matches a regular expression.

Exceptions

Handleable Exceptions

CX_SY_RANGE_OUT_OF_BOUNDS

◈ Cause: Illegal offset or length specification in the off and len.
Runtime Error: STRING_OFFSET_TOO_LARGE
CX_SY_REGEX_TOO_COMPLEX

◈ Cause: More information: Exceptions in Regular Expressions.
Runtime Error: REGEX_TOO_COMPLEX
CX_SY_STRG_PAR_VAL

◈ Cause: Regular expression in regex is empty.
Runtime Error: STRG_ILLEGAL_PAR

- - Predicate Function, matches

The example demonstrates the predicate function matches.

Source Code

REPORT demo_matches.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA email TYPE string VALUE `abc.def@ghi.jkl`.
    cl_demo_input=>request( CHANGING field = email ).
    IF matches( val   = email
                regex = `\w+(\.\w+)*@(\w+\.)+((\l|\u){2,4})` ).
      cl_demo_output=>display( 'Format OK' ).
    ELSEIF matches(
             val   = email
             regex = `[[:alnum:],!#\$%&'\*\+/=\?\^_``\{\|}~-]+`     &
                    `(\.[[:alnum:],!#\$%&'\*\+/=\?\^_``\{\|}~-]+)*` &
                    `@[[:alnum:]-]+(\.[[:alnum:]-]+)*`              &
                    `\.([[:alpha:]]{2,})` ).
      cl_demo_output=>display( 'Syntax OK but unusual' ).
    ELSE.
      cl_demo_output=>display( 'Wrong format!' ).
    ENDIF.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The program checks the formal correctness of an entered e-mail address by comparing it with regular expressions.

The first regular expression checks standard e-mail addresses without special characters, whereas the second regular expression performs a more lenient syntax check according to RFC 822.

Even the second check, which uses a relatively simple regular expression for the example, does not always work for all e-mail addresses which were possible according to RFC 822.

The program DEMO_VALIDATE_RFC_822_ADDRESS uses a regular expression taken from the Internet which recognizes all e-mail addresses allowed by RFC 822. The regular expression here was written originally for Perl and has more than 6000 characters. The program is therefore an example of how not to use regular expressions in ABAP.

2.2 Predicate Functions for Table-Like Arguments

The following predicate function can be used for table-like arguments:

line_exists

rel_exp - line_exists, Predicate Function

Syntax

... line_exists( table_exp ) ...

Effect

The predicate function line_exists checks whether the row of an internal table specified in the table expression table_exp exists and returns the appropriate truth value. Alongside single table expressions, table_exp can also handle chainings, whose result is a row of an internal table.

Within line_exists, an explicitly specified table key in the table row table_line of the table expression is handled in the same way as a free search key specified for this table key.

Notes

◈ The table expression is only used to check the existence of the specified row. No temporary result is created.
◈ The predicate function line_exists can be considered as a short form of the statement READ TABLE with the addition TRANSPORTING NO FIELDS following by sy-subrc being checked.
◈ The predicate function line_exists cannot be used to determine the row number in a table index of a search key, since table expressions do not fill the system field sy-tabix. The table function line_index can be used instead.
◈ If a search key specified in table_line in the table expression covers the initial part of a secondary table key without being specified explicitly after KEY, a syntax check warning is produced (which can be hidden by a pragma), since the function is generally quicker if the secondary key is specified explicitly.
◈ As in other use cases for table expressions, line_exists must be used carefully and no duplicate selections made. For example, line_exists should not be used to first check the existence of row and then read it. Instead, the table expression can be assigned to a field symbol and then sy-subrc checked. If the row in question usually exists, the table expression can be specified in the required operand position and the exception CX_SY_ITAB_LINE_NOT_FOUND caught.
◈ As well as assigning a table expression a default value for rows that are not found, there is also the option of checking the existences of rows.

Example

DATA flight_tab TYPE HASHED TABLE OF spfli
                     WITH UNIQUE KEY carrid connid.

SELECT *
       FROM spfli
       INTO TABLE @flight_tab.

IF line_exists( flight_tab[ carrid = 'LH'
                            connid = '0400' ] ).
  ...
ENDIF.

3. Predicative Method Calls

Syntax

... meth( ... ) ...

Effect

A predicative method call is a relational expression whose only operand is a functional method call meth( ... ). The result of the relational expression is true if the result of the functional method call is not initial and false if the result of the functional method call is initial. The result of the functional method call (the return value of the called function method) can have any data type. A check is made on the type-friendly initial value.

A predicative method call, like any relational expression, can be a full logical expression or part of a logical expression. This means it can be specified as a condition in control statements and other statements, as an argument in Boolean functions or conditional expressions, or in joins with Boolean operators.

Notes

◈ A predicative method call is a short form of the predicate expression:
... meth( ... ) IS NOT INITIAL ...
◈ Predicate methods whose return value has the type abap_bool are particularly well suited as predicative method calls. Calling a predicate method in a predicative method call simulates the call of a method whose return value has the real Boolean data type not present in ABAP.
◈ As usual, the functional method call meth( ... ) can be specified as a individual method or as a method chaining. If the first method called is an instance method, the functional method call can be introduced as usual using the instance operator NEW or the casting operator CAST.
◈ Alongside the dedicated predicate functions and an obsolete short form , predicative method calls are the only way of creating a relational expression using a single operand.
◈ For other expressions or data objects, the full predicate expression IS INITIAL or a comparison must be used to check the initial value and this is not normally recommended for many expressions.

Example

Exits the program if no SAP GUI is available. The called method check from the class CL_DEMO_SAP_GUI is a predicate method.

IF NOT cl_demo_sap_gui=>check( ).
  LEAVE PROGRAM.
ENDIF.

Predicative Method Calls

This example demonstrates predicative method calls.

Source Code

REPORT demo_predicative_method_call.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    IF cl_abap_demo_services=>is_production_system( ).
      cl_demo_output=>display(
         'This demo cannot be executed in a production system' ).
      LEAVE PROGRAM.
    ENDIF.

    DATA carrier TYPE spfli-carrid VALUE 'LH'.
    cl_demo_input=>request( CHANGING field = carrier ).

    DATA(out) = cl_demo_output=>new(
      )->next_section( 'IF' ).
    IF cl_demo_spfli=>get_spfli( to_upper( carrier ) ).
      out->write( 'Filled' ).
    ELSE.
      out->write( 'Not filled' ).
    ENDIF.

    out->next_section( 'COND'
      )->write( COND string(
                  WHEN cl_demo_spfli=>get_spfli( to_upper( carrier ) )
                    THEN `Filled`
                  ELSE `Not filled` )
      )->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The functional method IS_PRODUCTION_SYSTEM of the class CL_ABAP_DEMO_SERVICES is called predicatively in a control statement IF to verify that the current system is not a production system. The method called is a predicate method.

The functional method GET_SPFLI of the class CL_DEMO_SPFLI is then called predicatively once in a control statement IF and once in a conditional expression COND. In this case, it is not a predicate method. The relational expressions are true or false, depending on whether the internal table returned is empty or filled.

◉ log_exp - Boolean Operators and Parentheses

The Boolean operators

◈ AND
◈ OR
◈ EQUIV

join logical expressions; the operator

◈ NOT

negates a logical expression.

Logical expressions can be parenthesized explicitly using

◈ ( )
.

When combining multiple Boolean operators, the system implicitly parenthesizes all logical expressions that are not closed by explicit parentheses according to the following hierarchy (operator order, precedence). This expresses the strength of the connection between Boolean operators:

1. All Boolean operators NOT are combined with the adjacent logical expression to the right to a logical expression.

2. All logical expressions joined using AND are combined as one logical expression.

3. All logical expressions joined using OR are combined as one logical expression.

4. All logical expressions joined using EQUIV are combined as one logical expression.

The logical expressions of a parenthesis level are processed from the left to the right. If the value of a logical expression determines the total value of the parenthesis level, the remaining logical expressions are not evaluated. The dynamic parts of the expressions that are not evaluated, such as field symbols or reference variables, are not checked for validity in this case.

Notes

◈ If multiple logical expressions are joined, the explicit and implicit parentheses always produce exactly one logical expression that is either true or false.
◈ The smallest unit of a joined logical expression is always a relational expression.
◈ If functional methods are used as operands in the relational expressions in question, they are executed directly before the respective relational expression is evaluated. Any functional methods in a relational expression that does not need to be evaluated to get the result are not executed.

◈log_exp - NOT

Syntax

... NOT log_exp ...

Effect

The negation of a logical expression log_exp using NOT creates a new logical expression that is false if the logical expression log_exp is true and vice versa.

Notes

The operator NOT is a stronger join than AND, OR, and EQUIV.

The Boolean operator NOT must not be confused with the addition NOT of the relational operators BETWEEN, IN, and the predicate operator IS. The following syntax is one possible example:

... NOT operand NOT IN seltab ...

The first NOT is a Boolean operator that negates a comparison expression. The second NOT is a part of the comparison expression withe the relational operator IN. This is made clear using parentheses around the comparison expression:

... NOT ( operand NOT IN seltab ) ...

◈log_exp - AND

Syntax

... log_exp1 AND log_exp2 AND log_exp3 ...

Effect

The joining of multiple logical expressions log_exp using AND creates a new logical expression that is true when all logical expressions log_exp are true. If one of the logical expressions is false, then the join is also false.

Notes

● The operator AND is a weaker join than NOT and stronger than OR and EQUIV.
● The operator AND realizes an AND join. The negation of a join like this NOT corresponds to a NAND join (Not AND). It is true if at least one expression is false.
● The Boolean operator AND should not be confused with the addition AND of the relational operator BETWEEN.

◈log_exp - OR

Syntax

... log_exp1 OR log_exp2 OR log_exp3 ...

Effect

If multiple logical expressions log_exp are joined using with OR, a new logical expression is created which is true if at least one of the logical expressions log_exp is true. The join is false only if all logical expressions are also false.

Notes

● The operator OR provides a weaker join than NOT and AND provides a stronger join than EQUIV.
● The operator OR implements a logical OR join. Negating a join of this type using NOT is the same as a NOR join (Not OR). It is true if all expressions are false.

◈log_exp - EQUIV

Syntax

... log_exp1 EQUIV log_exp2 ...

Effect

Joining two logical expressions log_exp with EQUIV produces a new logical expression which is true if both expressions are true or both expressions are false. If one of the expressions is true and the other one is false, the join is false.

Within a parenthesis level, only two logical expressions can be joined using EQUIV.

Notes

● The operator EQUIV has a weaker join effect than NOT, AND, and OR.
● The operator EQUIV implements an equivalence join. Negating a join like this using NOT corresponds to an XOR join (eXclusive OR). It is true if one of the expressions is true and the other one is false.

Example

The condition in the IF statement is true if o1, o2, and o3, o4 are pair-wise either both equal or both unequal.

DATA: o1 TYPE i,
      o2 TYPE i,
      o3 TYPE i,
      o4 TYPE i.

...

IF o1 = o2 EQUIV o3 = o4.
  ...
ENDIF.

◈log_exp - ( )

Syntax

... ( log_exp ) ...

Effect

A full logical expression log_exp can be placed in parentheses. In the case of log_exp, this can be a join between multiple expressions using AND, OR, or EQUIV or a logical expression negated using NOT. A logical expression in parentheses is itself a logical expression.

Note

The smallest unit of a logical expression that can be placed in parentheses is a relational expression.

Example

A join of the logical expressions

NOT log_exp1 OR log_exp2 AND NOT log_exp3 AND log_exp4 EQUIV log_exp5

can be parenthesized explicitly as follows to stress the implicit parentheses:

1. Parentheses around the expressions with NOT
( NOT log_exp1 ) OR
log_exp2 AND ( NOT log_exp3 ) AND log_exp4 EQUIV log_exp5

2. Parentheses around the expressions with AND
( NOT log_exp1 ) OR
( log_exp2 AND ( NOT log_exp3 ) AND log_exp4 ) EQUIV log_exp5

3. Parentheses around the expressions with OR
( ( NOT log_exp1 ) OR
( log_exp2 AND ( NOT log_exp3 ) AND log_exp4 ) ) EQUIV log_exp5

4. Parentheses around the expressions with EQUIV
( ( ( NOT log_exp1 ) OR
( log_exp2 AND ( NOT log_exp3 ) AND log_exp4 ) ) EQUIV log_exp5 )

Changing the parentheses explicitly changes the meaning of the expression. For example, if explicit parentheses are set as follows in the above expression:

( NOT log_exp1 OR log_exp2 ) AND
NOT ( log_exp3 AND log_exp4 EQUIV log_exp5 )

produces the following the result after implicit parentheses are set:

( ( ( NOT log_exp1 ) OR log_exp2 ) AND
( NOT ( ( log_exp3 AND log_exp4 ) EQUIV log_exp5 ) ) )

1.2 Logical Functions

Logical functions belong to the predefined functions. They are divided into:

◈ Boolean functions
◈ Predicate functions

Note

The program DEMO_EXPRESSIONS shows you examples of how to use logical functions.

boolc, boolx, xsdbool - Boolean Functions 

Variants:

1. ... boolc( log_exp ) ...

2. ... boolx( bool = log_exp bit = bit ) ...

3. ... xsdbool( log_exp ) ...

Effect

The Boolean functions determine the truth value of a logical expression log_exp specified as an argument. For log_exp, any logical expression can be specified in accordance with the applicable rules. The return value of a Boolean function has a data type determined by the function and expresses the truth value of the logical expression using a value of this type.

Note

These functions can be viewed as a partial replacement for the Boolean data type for truth values not available in ABAP. In particular, xsdbool and (with restrictions) boolc can be used in many operand positions where input parameters of the type abap_bool of the type group ABAP are expected.

Variant 1

... boolc( log_exp ) ...

Effect

The function boolc returns a single-character character string of the type string. If the logical expression is true, "X" is returned. If the logical expression is false, a blank is returned. In principle, boolc is one of the processing functions with character-like results and can be specified in general expression positions and in character-like expression positions.

Notes

◈ If boolc requires return values other than "X" or " " (for example, "Y" and "N" or "1" and "0"), the result of boolc can be edited directly using the function translate or another suitable processing function.
◈ The result of boolc must not be compared in relational expressions with the constants abap_true and abap_false, since the comparison converts the latter from c to string and ignores any blanks. Comparisons of this type are not usually necessary. If a comparison of this type is required anyway, the function xsdbool can be used instead of boolc. The result of this function has the same ABAP type as abap_bool.
◈ If the logical expression is false, the result of boolc does not meet the condition IS INITIAL, since a blank is returned (not an empty string). If this is the required behavior, the function xsdbool can be used instead of boolc.
◈ If boolc is used in inappropriate places (as specified in the points above), a syntax warning is produced (which can be hidden using a pragma).

Example

The value 0, 1, or 2 is assigned to the variable bool_value, depending on the result of the logical expressions log_exp1 and log_exp2.

DATA bool_value TYPE i.

bool_value = strlen( condense( val = boolc( log_exp1 ) ) ) +
             strlen( condense( val = boolc( log_exp2 ) ) ).
Example

Calls a method, where the input parameter no_dialog is supplied with the character-like representation of the results of a predicate expression.

PARAMETERS word TYPE c length 30.
DATA result_tab TYPE cl_abap_docu=>search_results.

cl_abap_docu=>start(
  EXPORTING word           = word
            no_dialog      = boolc( sy-batch IS NOT INITIAL )
  IMPORTING search_results = result_tab ).

Variant 2

... boolx( bool = log_exp bit = bit ) ...

Effect

The function boolx returns a byte chain of the type xstring. If the logical expression is true, the byte chain is filled as if the function bit-set( bit ) were being executed. If the logical expression is false, the byte chain is filled as if the function bit-set( 0 ) were being executed. bit expects a data object of the type i. In principle, boolx is one of the bit functions and can be used in all positions where a bit expression is also allowed.

Note

The function boolx can be used for efficient saving of sequences of truth values.

Example

The result of the following bit expression is hexadecimal 55, the same as the calculated bit string 01010101.

DATA(result) = boolx( bool = 2 > 1 bit = 8 )
        BIT-OR boolx( bool = 2 < 1 bit = 7 )
        BIT-OR boolx( bool = 2 > 1 bit = 6 )
        BIT-OR boolx( bool = 2 < 1 bit = 5 )
        BIT-OR boolx( bool = 2 > 1 bit = 4 )
        BIT-OR boolx( bool = 2 < 1 bit = 3 )
        BIT-OR boolx( bool = 2 > 1 bit = 2 )
        BIT-OR boolx( bool = 2 < 1 bit = 1 ).

The bit expression above can be expressed using the following iteration with the operator REDUCE.

DATA(result) =
  REDUCE xstring( INIT x TYPE xstring
                  FOR j = 4 THEN j - 1 UNTIL j < 1
                  LET b1 = 2 * j b2 = 2 * j - 2 IN
                  NEXT x = x BIT-OR boolx( bool = 2 > 1  bit = b1 )
                             BIT-OR boolx( bool = 2 < 1  bit = b2 ) ).

Variant 3

... xsdbool( log_exp ) ...

Effect

Like boolc, the function xsdbool returns the value "X" for true and a blank for false. The data type of the return value, however, has the type c of the length 1 here.

The return value references the type XSDBOOLEAN from ABAP Dictionary. This type (which references the identically named domain with the type CHAR and length 1) is handled like a real Boolean type in serializations and deserializations to or from asXML and asJSON using CALL TRANSFORMATION: The XML or JSON values true and false are assigned to the values "X" and " " of this type.

xsdbool can be specified in general and character-like expression positions.

Notes

◈ The result of xsdbool can be used like a value of the type abap_bool and compared with the constants abap_true and abap_false.
◈ If the logical expression is false, the result of boolc meets the condition IS INITIAL, since the returned blank is the type-friendly initial value too.
◈ The result of xsdbool cannot usually be implemented directly using a processing function such as translate, since the trailing blanks here are ignored in text fields with the type c. The result of a false logical expression would be ignored. The result of the function boolc with the type string is better suited to transformations of this type.
◈ The abbreviation xsd stands for XML schema data types.

Example

This example sets the type and the value of the variable gui_flag declared inline using the Boolean function xsdbool. A predicative method call is specified as the argument of this function. The variable is then serialized to asXML and asJSON using the predefined identity transformation ID. This produces the value true or false. After being compared with the identically typed constant abap_false, the result of the serializations is either read or displayed.

The result would be very different if boolc were used instead of xsdbool. Firstly, the transformations would have a different result (since the values "X" and " " are not transformed to true or false); secondly, the logical expression gui_flag = abap_false would always be false (since abap_false loses its blank when converted to the type string).

DATA(gui_flag) = xsdbool( cl_demo_sap_gui=>check( ) ).

CALL TRANSFORMATION id SOURCE gui_flag = gui_flag
                       RESULT XML DATA(xml).

DATA(writer) =
  cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
CALL TRANSFORMATION id SOURCE gui_flag = gui_flag
                       RESULT XML writer.
DATA(json) = writer->get_output( ).

cl_demo_output=>write_xml( xml ).
cl_demo_output=>write_json( json ).

IF gui_flag = abap_false.
  cl_demo_output=>get( ).
ELSE.
  cl_demo_output=>display( ).
ENDIF.

2. Control Structures


The statements in a processing block are organized in control structures. These define statement blocks and control the progress of the program within a processing block. They determine the conditions for processing statement blocks and how often this happens. Control structures can be nested. Statement blocks in control structures can themselves contain control structures.

The following control structures exist:

◈ Sequence

A sequence consists of a statement block that is not defined explicitly by control statements. The statements it contains are processed once without conditions. The execution of a sequence can be suspended for a certain time using a WAIT statement.

◈ Branch (selection)

A branch comprises one or more statement blocks defined by control statements such as IF or CASE, and that are executed according to conditions.

◈ Loop (iteration)

A loop comprises a statement block that is defined by control statements such as DO or WHILE, and that can be executed several times.

There are also special control structures for exception handling.

Note

One obsolete control structure is ON CHANGE OF.

1.1 Branches

This section describes the branches defined using

◈ IF - ENDIF
◈ CASE - ENDCASE
◈ CASE TYPE OF - ENDCASE

Special types of branch are group control processing for internal tables and extracts and exception handling.

Note: The conditional operators COND and SWITCH can be used to implement branches in operand positions.

1.1.1 IF

Syntax

IF log_exp1.
  [statement_block1]
[ELSEIF log_exp2.
  [statement_block2]]
...
[ELSE.
  [statement_blockn]]
ENDIF.

Effect

These statements define a control structure which can contain multiple statement blocks statement_block of which a maximum of one will be executed in conjunction with logical expressions log_exp.

After IF and ELSEIF any logical expressions log_exp can be executed while the expressions statement_block stand for any statement blocks.

The logical expressions, beginning with the IFstatement, are checked from top to bottom and the statement block after the first is executed during the logical expression. If none of the logical expressions are true, the statement block after the ELSE statement is executed.

When the end of the statement block is reached or if no statement block is to be executed, the processing is continued after ENDIF.

Note: Furthermore, the conditional operator COND can also be used to implement branches in operand positions that are based on logical expressions.

Example

Converts a time output into the 12-hour format (see also Country-Specific Formats).

DATA time TYPE t.

fltime = sy-time.

IF time < '120000'.
  cl_demo_output=>display(
    |{ time TIME = ISO } AM| ).
ELSEIF time > '120000' AND
       time < '240000'.
  cl_demo_output=>display(
    |{ CONV t( time - 12 * 3600 ) TIME = ISO } PM| ).
ELSE.
  cl_demo_output=>display(
    |High Noon| ).
ENDIF.

◈ ELSEIF 

Syntax

ELSEIF log_exp.

Effect

These statements define a control structure which can contain multiple statement blocks statement_block of which a maximum of one will be executed in conjunction with logical expressions log_exp.

After IF and ELSEIF any logical expressions log_exp can be executed while the expressions statement_block stand for any statement blocks.

The logical expressions, beginning with the IFstatement, are checked from top to bottom and the statement block after the first is executed during the logical expression. If none of the logical expressions are true, the statement block after the ELSE statement is executed.

When the end of the statement block is reached or if no statement block is to be executed, the processing is continued after ENDIF.

◈ ELSE 

Syntax

ELSE.

Effect

These statements define a control structure which can contain multiple statement blocks statement_block of which a maximum of one will be executed in conjunction with logical expressions log_exp.

After IF and ELSEIF any logical expressions log_exp can be executed while the expressions statement_block stand for any statement blocks.

The logical expressions, beginning with the IFstatement, are checked from top to bottom and the statement block after the first is executed during the logical expression. If none of the logical expressions are true, the statement block after the ELSE statement is executed.

When the end of the statement block is reached or if no statement block is to be executed, the processing is continued after ENDIF.

◈ ENDIF 

Syntax

ENDIF.

Effect

These statements define a control structure which can contain multiple statement blocks statement_block of which a maximum of one will be executed in conjunction with logical expressions log_exp.

After IF and ELSEIF any logical expressions log_exp can be executed while the expressions statement_block stand for any statement blocks.

The logical expressions, beginning with the IFstatement, are checked from top to bottom and the statement block after the first is executed during the logical expression. If none of the logical expressions are true, the statement block after the ELSE statement is executed.

When the end of the statement block is reached or if no statement block is to be executed, the processing is continued after ENDIF.

1.1.2 CASE

Syntax

CASE operand.
  [WHEN operand1 [OR operand2 [OR operand3 [...]]].
    [statement_block1]]
  ...
  [WHEN OTHERS.
    [statement_blockn]]
ENDCASE.

Effect

Case distinction. These statements define a control structure that can contain multiple statement blocks statement_block1, ..., statement_blockn, of which no more than one is executed depending on the value of the operand operand.

Starting with the first WHEN statement, the content of the operand in operand is compared with the content of one of the operands operand1, operand2, ... from the top down. The statement block is executed after the first identical instance is found. If no matches are found, the statement block is executed after the statement WHEN OTHERS.

◈ The operand operand after CASE is a general expression position.

◈ The operands operand1, operand2, ... after WHEN are extended functional operand positions in which, however, table expressions cannot be specified. This property of operand positions is obsolete and should no longer be used.

If the end of the executed statement block is reached or no statement block is executed, processing continues after ENDCASE.

The contents are compared as illustrated in the following logical expression:

operand = operand1 [OR operand = operand2
                   [OR operand = operand3 [...]]]

For the comparison, the comparison rules for comparisons between any operands apply, depending on the data types of the operands involved.

Notes

◈ For operand, the current value is used in every comparison. This may differ from the starting value if operand is a variable that is changed in a functional method specified after a WHEN statement.

◈ A CASE control structure is somewhat faster than a semantically equivalent IF control structure.

◈ Functional methods and certain predefined functions can be specified after WHEN, however this should be avoided. Specify constant values, for example, in the operand positions after WHEN.

◈ In a special case, a control structure introduced using CASE TYPE OF makes it possible for the type of object reference variables to be checked.

◈ The conditional operator SWITCH can also be used to make case distinctions in operand positions.

◈ A statement cannot be placed between the statement CASE and the first statement WHEN. In classes, this produces a syntax error; outside classes, obsolete syntax of this type produces a syntax warning.

Example

Branches the program flow depending on the function code in system field sy-ucomm.

CASE sy-ucomm.
  WHEN 'BACK'.
    LEAVE TO SCREEN 100.
  WHEN 'CANCEL'.
    LEAVE SCREEN.
  WHEN 'EXIT'.
    LEAVE PROGRAM.
  WHEN OTHERS.
    MESSAGE '...' TYPE 'E'.
ENDCASE.

Example

This example demonstrates that the calculation type of an arithmetic expression after CASE is determined only by its operands. The calculation type after the first CASE statement is i and the result of the calculation is 0. The comparison with the value 0 after WHEN is true regardless of its data type. The calculation type of the comparison after IF is decfloat34, however, and the result is false. To force a specific calculation type after CASE, a further operand can be added as shown in the second CASE statement. This operand is not involved in the calculation.

DATA:
  inum1 TYPE i VALUE 1,
  inum2 TYPE i VALUE 3,
  decf  TYPE decfloat34 VALUE 0.

CASE  inum1 / inum2.
  WHEN decf.
    cl_demo_output=>write_text( 'In CASE equal' ).
ENDCASE.

IF decf <> inum1 / inum2.
  cl_demo_output=>write_text( 'In IF not equal' ).
ENDIF.

CASE  inum1 / inum2 + decf.
  WHEN decf.
    cl_demo_output=>write_text( 'In CASE equal' ).
  WHEN OTHERS.
    cl_demo_output=>write_text( 'In CASE not equal' ).
ENDCASE.

cl_demo_output=>display( ).

◈ WHEN

Syntax

WHEN { {operand1  [OR operand2 [OR operand2 [...]]]}
     | {OTHERS} }.

Effect

Case distinction. These statements define a control structure that can contain multiple statement blocks statement_block1, ..., statement_blockn, of which no more than one is executed depending on the value of the operand operand.

Starting with the first WHEN statement, the content of the operand in operand is compared with the content of one of the operands operand1, operand2, ... from the top down. The statement block is executed after the first identical instance is found. If no matches are found, the statement block is executed after the statement WHEN OTHERS.

◈ The operand operand after CASE is a general expression position.

◈ The operands operand1, operand2, ... after WHEN are extended functional operand positions in which, however, table expressions cannot be specified. This property of operand positions is obsolete and should no longer be used.

If the end of the executed statement block is reached or no statement block is executed, processing continues after ENDCASE.

The contents are compared as illustrated in the following logical expression:

operand = operand1 [OR operand = operand2
                   [OR operand = operand3 [...]]]

For the comparison, the comparison rules for comparisons between any operands apply, depending on the data types of the operands involved.

◈ ENDCASE

Syntax

ENDCASE.

Effect

The statement ENDCASE closes a case distinction introduced using CASE or CASE TYPE OF.

1.1.3 CASE TYPE OF

Syntax

CASE TYPE OF oref
  [WHEN TYPE class|intf [INTO target1].
    [statement_block1]]
  [WHEN TYPE class|intf [INTO target2].
    [statement_block2]]
  ...
  [WHEN OTHERS.
    [statement_blockn]]
ENDCASE.

Addition:

... INTO target

Effect

Special case distinction for object reference variables. This form of a control structure introduced using CASE checks the dynamic type of a non-initial object reference variable and the static type of an initial object reference variable oref. oref expects an object reference variable with the static type of a class or of an interface. oref is a general expression position.

A class class or an interface intf valid in this place must be specified after WHEN TYPE. The first statement block statement_block is executed for which the class class or the interface intf is more general than or equal to the

◈ dynamic type of a non-initial object reference variable oref
◈ static type of an initial object reference variable oref

If this does not apply to any class class or interface intf, the statement block is executed after WHEN OTHERS. No object type class or intf can be specified if it is possible to identify statically that it does not meet the condition.

Notes

◈ A case distinction using CASE TYPE OF is a different spelling of the following branch using IF and the predicate expression IS INSTANCE OF and the corresponding rules and notes apply:

IF oref IS INSTANCE OF class|intf.
  [statement_block1]
ELSEIF oref IS INSTANCE OF class|intf.
  [statement_block2]
...
ELSE.
  [statement_blockn]
ENDIF.

◈ The control structure must specify more specific classes class or interfaces intf before more general classes or interfaces to enable the associated statement block to be executed.

Addition

... INTO target

Effect

For every statement WHEN TYPE of a case distinction introduced using CASE TYPE OF, a target variable target can be specified after the optional addition INTO as follows:

◈ An existing object reference variable ref whose static type is more general or equal to the class class or interface intf specified in the statement.

◈ An inline declaration DATA(ref). In this case, an object reference variable with the static type of the class class or the interface intf is declared.

If the addition INTO is specified in the first WHEN statement that meets the condition, the reference oref is assigned to ref before the statement block is executed. Here, both up casts and down casts can be performed.

Note

A statement

WHEN TYPE class|intf INTO ref.
is a semantically identically short form of

WHEN TYPE class|intf.
  ref = oref.
A statement

WHEN TYPE class|intf INTO DATA(ref).
is a semantically identically short form of

WHEN TYPE class|intf.
  DATA(ref) = CAST class|intf( oref ).

◆ WHEN TYPE 

Syntax

WHEN TYPE ... [INTO target].

Effect

Special case distinction for object reference variables. This form of a control structure introduced using CASE checks the dynamic type of a non-initial object reference variable and the static type of an initial object reference variable oref. oref expects an object reference variable with the static type of a class or of an interface. oref is a general expression position.

A class class or an interface intf valid in this place must be specified after WHEN TYPE. The first statement block statement_block is executed for which the class class or the interface intf is more general than or equal to the

◈ dynamic type of a non-initial object reference variable oref
◈ static type of an initial object reference variable oref

If this does not apply to any class class or interface intf, the statement block is executed after WHEN OTHERS. No object type class or intf can be specified if it is possible to identify statically that it does not meet the condition.

◆ Case Distinction CASE TYPE OF for Exceptions 

This example demonstrates the case distinction CASE TYPE OF for exception classes.

Source Code

REPORT demo_case_type_of_exception.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
  PRIVATE SECTION.
    CLASS-DATA out TYPE REF TO if_demo_output.
    CLASS-METHODS my_sqrt IMPORTING num TYPE any
                          RAISING   cx_dynamic_check.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA number TYPE string.
    out = cl_demo_output=>new( ).
    cl_demo_input=>request( CHANGING field = number ).
    TRY.
        my_sqrt( number ).
      CATCH cx_root INTO DATA(exc).
        CASE TYPE OF exc.
          WHEN TYPE cx_sy_arithmetic_error.
            out->display( 'Arithmetic error' ).
          WHEN TYPE cx_sy_conversion_error.
            out->display( 'Conversion error' ).
          WHEN OTHERS.
            out->display( 'Other error' ).
        ENDCASE.
    ENDTRY.
  ENDMETHOD.
  METHOD my_sqrt.
    DATA(sqrt) = sqrt( CONV decfloat34( num ) ).
    out->display( sqrt ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

Non-specific exceptions of the superclass CX_DYNAMIC_CHECK can be propagated from a method my_sqrt. The actual exception class is determined from the calling method using the case distinction CASE TYPE OF.

◆ Case Distinction CASE TYPE OF for RTTI 

This example demonstrates the case distinction CASE TYPE OF for type description classes.

Source Code

REPORT demo_case_type_of_rtti.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main IMPORTING param TYPE any.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA(typedescr) = cl_abap_typedescr=>describe_by_data( param ).
    CASE TYPE OF typedescr.
      WHEN TYPE cl_abap_elemdescr INTO DATA(elemdescr).
        cl_demo_output=>write( elemdescr->type_kind ).
      WHEN TYPE cl_abap_structdescr INTO DATA(structdescr).
        cl_demo_output=>write( structdescr->components ).
      WHEN TYPE cl_abap_tabledescr INTO DATA(tabledescr).
        cl_demo_output=>write( tabledescr->table_kind ).
      WHEN OTHERS.
        cl_demo_output=>write( 'Not supported' ).
    ENDCASE.
  ENDMETHOD.
ENDCLASS.

DATA: elem TYPE i,
      BEGIN OF struct,
        comp1 TYPE i,
        comp2 TYPE i,
      END OF struct,
      itab LIKE STANDARD TABLE OF struc WITH EMPTY KEY,
      dref TYPE REF TO i.

START-OF-SELECTION.
  demo=>main( elem ).
  demo=>main( struct ).
  demo=>main( itab ).
  demo=>main( dref ).

  cl_demo_output=>display( ).

Description

Actual parameters of various types are passed to the generically typed parameter param of the method main and an RTTI type description object is created that points to the general object reference variable typedescr. The case distinction CASE TYPE OF is used to defined a more specific RTTI type description class that matches the type. An online declaration after the addition INTO of the statement WHEN TYPE is used to create an object reference variable of this static type and is assigned the reference to the type description object. Special attributes of the type description objects are accessed in the associated statement blocks.

1.2 Loops

This section describes the loops defined using

➧ DO - ENDDO, and
➧ WHILE - ENDWHILE

Other keywords for defining loops are:

➧ LOOP - ENDLOOP, and
➧ PROVIDE - ENDPROVIDE for loops through internal tables and
➧ SELECT - ENDSELECT for loops through the result set of a database access

1. DO

Syntax

DO [n TIMES].
  [statement_block]
ENDDO.

Addition:

... n TIMES

Effect

Unconditional loop. The statements DO and ENDDO define a control structure, which can contain a closed statement block statement_block.

Without the addition n TIMES, the statement block is repeated until it is exited using one for the statements for leaving loops. In particular, the statement EXIT is ideal for exiting a loop completely. Within the statement block, the system field sy-index contains the number of previous loop passes, including the current pass. In nested loops, sy-index always refers to the current loop.

Notes

◉ If the addition n TIMES is not specified, the loop has to be terminated by a statement; otherwise the loop is processed endlessly. The profile parameter rdisp/max_wprun_time limits the maximum execution time of an ABAP program. If this is exceeded, the program is ended by the runtime environment.

◉ If DO loops are used to construct values or fill internal tables, they can probably be expressed more elegantly using conditional iterations with FOR in constructor expressions.

◉ The obsolete addition varying can be used to process a sequence of data objects in the memory.

Addition

... n TIMES

The addition n TIMES limits the amount of loop passes in a DO loop. n is a numeric expression position of operand type i.

The number value of n when entering the loop determines the maximum amount of passes of the statement block. The control structure ignores changes to the value n within the loop. If n contains a value less than or equal to 0, the statement block is not executed.

Example

Calculates and displays the first ten square numbers in a DO loop.

DATA square TYPE i.

DO 10 TIMES.
  square = ipow( base = sy-index exp = 2 ).
  cl_demo_output=>write( |{ sy-index } { square }| ).
ENDDO.
cl_demo_output=>display( ).

ENDDO

Syntax

ENDDO.

Effect

The statement ENDDO closes a loop started using DO.

2. WHILE

Syntax

WHILE log_exp
  [statement_block]
ENDWHILE.

Effect

Conditional loop. The statements WHILE and ENDWHILE define a control structure that can contain a closed statement block statement_block. After WHILE, any logical expression log_exp can follow.

The statement block is repeated as long as the logical expression is true, or until it is exited with one of the statements to leave loops. In particular, the statement EXIT is ideal for exiting a loop completely. Within the statement block, the system field sy-index contains the number of previous loop passes, including the current pass. In nested loops, sy-index always refers to the current loop.

Notes

◈ The profile parameter rdisp/max_wprun_time limits the maximum execution time of an ABAP program. If this runtime is exceeded because the logical expression is never false and the loop is not exited in any other way, the program is ended by the runtime environment.

◈ If WHILE loops are used to construct values or fill internal tables, they can probably be expressed more elegantly using conditional iterations with FOR in constructor expressions.

◈ The obsolete addition VARY can be used to process a sequence of data objects in the memory.

Example

Replaces all blanks with hyphens in a character-like data object text. Instead of the loop shown here for demonstration purposes, in a production program the addition ALL OCCURRENCES of the statement REPLACE or the predefined function replace with the value 0 for the argument occ should be used for this task.

DATA text TYPE string VALUE `One Two Three`.

WHILE sy-subrc = 0.
  REPLACE ` ` IN text WITH `-`.
ENDWHILE.

ENDWHILE 

Syntax

ENDWHILE.

Effect

The statement ENDWHILE closes a loop introduced using WHILE.

1.3 Program Interruption

This section describes how programs are usually interrupted using the statement WAIT. A special variant is explained in the context of asynchronous RFC.

WAIT UP TO 

Syntax

WAIT UP TO sec SECONDS.

Effect

This statement interrupts the execution of the program by the number of seconds specified in sec. sec is a numeric expression position of operand type i to which positive numbers (and 0) can be passed. The unit of the number in sec is seconds and the time resolution is one millisecond. After the specified time has passed, the program continues with the statement following WAIT. When used, this statement always changes the work process.

Return Codes

This statement always sets sy-subrc to 0.

Notes

◈ Each time this variant of the statement WAIT is used, a database commit is triggered (except in updates). For this reason, WAIT must not be used between Open SQL statements that open or close a database cursor.

◈ The variants WAIT FOR ASYNCHRONOUS TASKS, WAIT FOR MESSAGING CHANNELS, and WAIT FOR PUSH CHANNELS of this statement are useful only when used in combination with callback routines in aRFC, AMC, or APC. The variant shown here does not wait for callback routines.

Exceptions

Non-Handleable Exceptions

◈ Cause: Negative time specified for sec.
Runtime Error: WAIT_ILLEGAL_TIME_LIMIT

3. Exception Handling


Exceptions are events in the execution of an ABAP program that interrupt the program when it is not possible for the program to continue in a meaningful way. Exception handling enables a response to be made to these events.

Exception situations can be recognized either by the program or by the runtime environment. When an exception situation is recognized, either the ABAP program or the runtime environment raises an exception. Exceptions in the ABAP runtime environment are generally caused by error situations that cannot be predicted by the static program check.

Exceptions are either handleable or non-handleable.
  • Handleable exceptions are class-based. They are predefined in the system, or custom exceptions can be defined. They are raised either by the ABAP runtime environment or in an ABAP program using
    • the statement RAISE EXCEPTION
    • THROW specified in a conditional expression
  • and can be handled using TRY - CATCH - ENDTRY. As well as these, non-class-based exceptions and (obsolete) catchable runtime errors also exist.
  • Non-handleable exceptions are raised only by the ABAP runtime environment. They are a response to error situations that cannot generally be handled meaningfully in a program.
Unhandled exceptions (all non-handleable exceptions and all handleable exceptions not caught in the program) produce a runtime error, which means that the program is terminated with a short dump.

3.1 Class-Based Exceptions

Class-based exceptions are realized as instances of exception classes. Exception classes are either predefined globally in the system or can be defined by the user (globally or locally). Class-based exceptions are raised either by the ABAP runtime environment or by a program.

◈ Exception situations recognized by the system, and whose causes can be handled in the program, raise predefined class-based exceptions.
◈ The exceptions of all exception classes visible in a program can be raised by the statement RAISE EXCEPTION and by the addition THROW in conditional expressions.
◈ Class-based exceptions can be declared in the interface of procedures. For local procedures, this is done using the addition RAISING of the statements METHODS and FORM. In Class Builder and Function Builder, this done by selecting exception classes when defining exceptions in the interface. The declared exceptions can occur at the call position of a procedure if the exception is not handled in the procedure.

When an exception is raised, an exception object can be created, whose attributes contain information about the error situation. A class-based exception can be handled in a TRY control structure. The TRY block defines a protected area, whose exceptions can be handled in subsequent CATCH blocks. The statement RETRY enables a complete TRY block to be repeated after an exception. Because all exception classes are subclasses of common superclasses, the associated exceptions can be handled at the same time by handling the respective superclass. The system propagates the exception object or the class-based exception until the exception is handled or an interface is violated.

There are two different exception handling cases:

1. The context in which the exception was raised is deleted completely before or after handling. This removes all procedures from the memory (and also their local data, which was called from the handler context and which caused the exception); handling is resumed depending on how the handler is exited.

2. The context in which the exception was raised is retained and the program is resumed after the statement that raised the exception.

A prerequisite for the second case are resumable exceptions. These exceptions must be raised with the addition RESUMABLE of the statement RAISE EXCEPTION and declared using the addition RESUMABLE in the interface of the procedures from which they were propagated. The statement RESUME is used to resume the program.

3.1.1 Exception Classes

◐ Exception Categories

Exception classes are subclasses of the following global classes:

1. CX_STATIC_CHECK
2. CX_DYNAMIC_CHECK
3. CX_NO_CHECK

The common superclass for these classes is CX_ROOT. Assignment to one of these three superclasses determines the exception category, which specifies whether an exception must be explicitly declared in the procedure interface when propagating from a procedure, and how the declaration is checked:

◈ If exceptions defined using subclasses of CX_STATIC_CHECK are propagated from a procedure, they must be explicitly declared in the interface of the procedure. The syntax check makes a static check to determine whether all exceptions raised in the procedure using RAISE EXCEPTION or the addition THROW in a conditional expression or declared in the interfaces of called procedures are either handled using CATCH or declared explicitly in the interface, and produces a warning if this is not the case.

◈ If exceptions defined using subclasses of CX_DYNAMIC_CHECK are propagated from a procedure, they must be explicitly declared in the interface of the procedure. However, this is not checked statically by the syntax check; instead, it is checked dynamically at the point in time when such an exception is propagated from a procedure.

◈ Exceptions that are defined using subclasses of CX_NO_CHECK may not be explicitly declared in the interface of the procedure. Class CX_NO_CHECK and its subclasses are always implicitly declared and are always propagated, and all resumability is retained.

If an exception not declared in the interface of a procedure is propagated from the procedure, the interface is violated and an exception of the predefined class CX_SY_NO_HANDLER is raised in the call position of the procedure. The exception object of the exception contains a reference to the original exception in the attribute PREVIOUS.

Notes

◈ Using Exception Categories

     ◈ As a rule, exceptions that are raised in a procedure should be handled there or declared in the interface for the procedure to declare to the caller which exceptions are to be expected. A syntax check to verify this is run on exceptions from the CX_STATIC_CHECK category. This category is always warranted if a procedure is to be forced to handle an exception or to at least explicitly forward it. However, if an exception can be prevented by prior checks, exceptions of the CX_DYNAMIC_CHECK category are preferable.

     ◈ If the program logic can eliminate potential error situations, the corresponding exceptions do not have to be handled or declared in the interface. This is the case, for example, if, prior to a division, there is an explicit requirement for the denominator not to equal zero (precondition). In this case, exceptions from the CX_DYNAMIC_CHECK category can and should be used. These exceptions only need to be handled and declared if their occurrence cannot be otherwise prevented. In well modeled applications, exceptions are generally prevented by incorporating appropriate conditions in program code and CX_DYNAMIC_CHECK category should then be the most frequently used exception category.
   
     ◈ For exception situations that can occur at any time and that cannot be handled directly, the CX_NO_CHECK category can be used. Otherwise, all exceptions that can be raised due to resource bottlenecks would have to be caught or declared. These exceptions would then have to be specified in practically every interface, which would result in more complex programs lacking in clarity.

◈ Most predefined CX_SY_... exceptions for error situations in the runtime environment are subclasses of CX_DYNAMIC_CHECK. As a result, not every potential exception of every ABAP statement needs to be handled or declared. Only those whose occurrence cannot be prevented need to be handled or declared.

◈ The caller of a procedure must anticipate that the procedure propagates exceptions from category CX_NO_CHECK in addition to explicitly declared exceptions. Some of the predefined CX_SY_... exceptions for error situations in the runtime environment are subclasses of CX_NO_CHECK.

◈ Interface violations normally only occur for exceptions from category CX_DYNAMIC_CHECK, since exceptions from category CX_STATIC_CHECK are checked first by the syntax check and exceptions from category CX_NO_CHECK can be raised for any interface.

◈ Whether an exception is resumable is not specified as an attribute of the exception class; instead it is defined by the addition RESUMABLE of the statement RAISE EXCEPTION or the addition THROW in a conditional expression when the exception is raised. This attribute can be lost for exceptions of type CX_STATIC_CHECK and CX_DYNAMIC_CHECK during propagation of parameter interfaces of procedures, if the exceptions are not also declared there with RESUMABLE.

◐ Definition of Exception Classes

Exception classes can be defined globally in Class Builder or locally in a program. The names of global exception classes are prefixed with CX_, or YCX_ and ZCX_ in the case of exception classes created in customer systems. There is a set of predefined global exception classes, such as those prefixed with CX_SY_,, whose exceptions are raised in exception situations in the runtime environment.

All exception classes inherit the following instance methods from CX_ROOT:

◈ GET_TEXT and GET_LONGTEXT return the exception text (short text and long text) as return values of type string. These methods are defined in the interface IF_MESSAGE implemented in CX_ROOT and can be addressed using the identically named alias name for the class.
◈ GET_SOURCE_POSITION returns the program name, the name of the include program if applicable, and the line number of the statement that raised the exception.

All exception classes inherit the following instance attributes from CX_ROOT:

◈ TEXTID of type SCX_T100KEY for a key for the database table T100 or of the type SOTR_CONC for a key for OTR (Online Text Repository) (that defines the exception text). This is normally set by the constructor and evaluated with GET_TEXT.
◈ PREVIOUS of reference type CX_ROOT, which can contain a reference to a previous exception. This is normally set by the constructor.
◈ IS_RESUMABLE of type ABAP_BOOL, which is used in a CATCH or CLEANUP block to show whether the exception is resumable, that is whether a CATCH block can be exited using RESUME.

For global exception classes, Class Builder generates a non-modifiable instance constructor that has optional input parameters for all non-private attributes and that sets these attributes to the value of the input parameters. The ID of the required exception text can be passed to TEXTID. In the case of local exception classes, there are no special rules for the instance constructor.

Notes

◈ Instances of exception classes are generally created when exceptions are raised, but can also be instantiated using CREATE OBJECT.
◈ The instance constructor of an exception class should not raise any exceptions. However, if an exception is raised in the instance constructor after an exception was raised during instantiation of the exception class, and the exception cannot be handled there, this or (if propagation is unsuccessful) the exception CX_SY_NO_HANDLER replaces the exception originally raised.
◈ Additional methods and attributes can be defined in exception classes, for example to transport additional information about an error situation to the handler. The user-defined attributes should be read-only (READ-ONLY).

◐ Exception Texts

Each exception is assigned a text that can be given parameters using attributes and that describes the exception situation. This text is displayed in the short dump of the runtime error if the exception is not handled. If the exception is handled in the program, the text can be read using the method GET_TEXT of the interface IF_MESSAGE, implemented by every exception class. If applicable, the long text can be read using the method GET_LONG_TEXT. A global exception class has a predefined exception text that has the same name as the exception class. This predefined text can be edited and further exception texts can be defined.

From a technical perspective, each exception text is defined using an identically named constant in the public visibility section of the exception class and this constant defines its properties. The instance constructor of an exception class has an input parameter TEXTID to which a constant like this can be passed when the exception is raised. This then determines the exception text. If the parameter is not passed, the predefined exception text with the same name as the exception class is used.

The exception texts of an exception class are usually defined by referencing messages in the table T100. In predefined system classes, OTR (Online Text Repository) texts can also be used.

◈ Messages as Exception Texts
◈ Exception Texts for System Classes

Notes

◈ The names of the exception texts or the associated constants should be seen as keys of the texts available for an exception class. This means that when an exception is raised, only the exception class constants of the same name should be passed to the TEXTID parameter to identify the exception text.

◈ The way in which the exception texts (namely the associated constants and the instance constructor) are edited depends to a large extent on whether Class Builder in ABAP Workbench or the source code editor in the ABAP Development Tools (ADT) are used.

◆ Messages as Exception Texts 

The exception texts of an exception class are usually defined by referencing messages from the table T100. The exception class must implement the interface IF_T100_MESSAGE before the messages can be used as exception texts. Exception texts are assigned to messages using constant structures in the public visibility section of the exception class. Each statically predefined exception text has an identically named constant with the structured data type SCX_T100KEY from ABAP Dictionary. The individual components represent the message class, the message number, and the attributes assigned to the placeholders. The way the exception texts are edited depends on the tool in question:

◈ Exception texts can be created on the Texts tab in Class Builder in ABAP Workbench and then assigned a message class and message number by selecting a message text. Furthermore, the placeholders "&1" to "&4" or "&" of the message can be assigned to public attributes of the exception class. Class Builder creates the associated constant structure in the public visibility section with the name of the exception text and generates a matching instance constructor.

◈ The exception texts are edited in source code editors in the ABAP Development Tools (ADT). An exception class is defined by the fact that it inherits from one of the superclasses CX_STATIC_CHECK, CX_DYNAMIC_CHECK, or CX_NO_CHECK. Classes of this type include the interface IF_T100_MESSAGE by default and the constructor is generated accordingly. The code template textIdExceptionClass can be used to create a constant structure in the visibility section for each exception text. The components of this structure define the properties of the message.

The input parameter TEXTID of the instance constructor of the exception class has the same data type as the attribute T100KEY of the interface IF_T100_MESSAGE, which references SCX_T100KEY. When an exception with messages as exception texts is raised, the constructor can be passed a structure with information about the message to be used as the exception text. It is strongly recommended that only the constants that exist in the class for the predefined exception texts are passed to TEXTID. This selects a predefined text for the exception. The attributes assigned to the placeholders "&1" to "&4" or "&" of a message can be assigned values using the input parameters of the same name of the instance constructor. The placeholders are replaced by the content of the assigned attributes when the exception is raised.

Notes

◈ Exception texts associated with messages can be sent to the program user using the statement MESSAGE oref when the exception is handled.
◈ The system interface IF_T100_DYN_MSG (which includes IF_T100_MESSAGE) is available for associating a message with an exception class. See Exception Classes for Messages.
◈ When an exception class is created in Class Builder in which the interface IF_T100_DYN_MSG (and hence IF_T100_MESSAGE) is included, it is recommended that this is specified when creating the exception class by setting the appropriate flag. This ensures that the constructor is generated immediately and as required. Otherwise, the constructor for internal exception texts is generated and must be generated again once it is included in the interface by choosing Utilities → Clean Up → Constructor in the menu.
◈ From a technical perspective, any structure of type SCX_T100KEY whose components specify any message of table T100 can be passed to the input parameter TEXTID of the instance constructor. However, this is strongly discouraged because an exception should only be raised with specific texts when using the parameter TEXTID.

◆ Exception Texts for System Classes 

This function is for internal use only.
Do not use it in application programs.

If an exception class does not implement the interface IF_T100_MESSAGE, texts stored in OTR (Online Text Repository) are used as exception texts. These texts can contain any number of placeholders. Each placeholder is represented by the name of an attribute from the exception class, which is enclosed by "&" characters. When the exception is raised, the placeholders are replaced by the content of the attributes. Exception texts can only be edited in one tool:

◈ Any exception texts can be defined on the Texts tab in Class Builder in ABAP Workbench. When saved, they are stored under a UUID in OTR. For each exception text, Class Builder creates an identically named constant with the data type SOTR_CONC from ABAP Dictionary. This constant contains the UUID and generates an appropriate instance constructor.
◈ ABAP Development Tools (ADT) do not support any exception texts from OTR. No exception texts can be defined for OTR and no UUIDs are created.

The input parameter TEXTID of the instance constructor has the data type SOTR_CONC. When an exception with these exception texts is raised, a UUID that identifies a text from OTR can be passed to the constructor. It is strongly recommended that only the constants that exist in the class for the predefined exception texts are passed. This selects a predefined text for the exception. The attributes assigned to the placeholders of the text can be assigned values using the input parameters of the same name of the instance constructor.

Notes

◈ OTR texts should only occur in predefined exception classes for system exceptions and should not be used in self-defined exception classes. One example are the predefined exception classes for ABAP statements.
◈ An exception text from OTR can be displayed and edited on the Texts tab in Class Builder, but the reference between the class and the text is made by the UUID saved in the identically named constant.
◈ From a technical perspective, any UUID that specifies any text from OTR can also be passed to the input parameter TEXTID of the instance constructor. However, this is strongly discouraged because an exception should only be raised with specific texts.
◈ If the implementation of the interface IF_T100_MESSAGE is deleted from an existing exception class using messages as exception texts, the instance constructor can be modified using the function Utilities → Clean Up → Constructor in Class Builder in ABAP Workbench.

◐ Exception Classes for Messages

The exception texts described in the previous section are assigned to an exception class statically and express a predefined semantic meaning. This applies to both categories of exception texts, namely messages as exception texts and internal exception texts from OTR.

Another frequent situation is when a message from the table T100 is passed to the exception object when an exception is raised. This is the case, for example, when a non-class-based exception raised using MESSAGE RAISING is handled or a message of a function module is caught using error_message and then passed as a class-based exception.

The predefined exception texts selected using the input parameter TEXTID of the instance constructor are unsuitable in these cases. Instead, the following are available:

◈ The system interface IF_T100_DYN_MSG
◈ The addition MESSAGE of the statement RAISE EXCEPTION and of the addition THROW in a conditional expression.

Exception objects of exception classes that include the system interface IF_T100_DYN_MSG can be associated with any message from the table T100 using the addition MESSAGE. Exception objects of exception classes that include only the system interface IF_T100_MESSAGE can also be associated with messages using the addition MESSAGE (but with restrictions). The latter option is intended only for exception classes that were designed for generic use with exception texts before IF_T100_DYN_MSG was implemented. In other cases, the concept of exception texts is strictly divided from the association of a messages with special exception classes. More specifically, the addition MESSAGE cannot be combined with the input parameter TEXTID.

3.1.2 System Response After a Class-Based Exception

A class-based exception can be raised in a statement block for one of the following reasons:

◈ The exception is raised explicitly using the statement RAISE EXCEPTION or the addition THROW in a conditional expression.
◈ The exception is raised implicitly by the ABAP runtime environment.

In both cases, the occurrence of a class-based exception interrupts the sequential processing of the current processing block, and the system responds as follows:

◈ If the exception is raised when a TRY block of a TRY control structure is executed, an appropriate CATCH block is searched as a handler. Execution of each TRY block opens a context, also called a protected area, into which the execution of other TRY blocks can be embedded. Usually, it is embedded by means of the call of procedures, less frequently by means of nesting TRY blocks in the source text. Starting at the position where the exception is raised, the system scans the TRY control structures of the participating TRY blocks from the inside to the outside for the first CATCH block, in which the exception class or one of its superclasses appears. If it finds a CATCH block, there are two possible cases:

◈ If the addition BEFORE UNWIND is not declared in the statement CATCH, the context in which the exception was raised is first deleted, including all called procedures and their local data. The CATCH block is then executed.

◈ If the addition BEFORE UNWIND is declared in the statement CATCH, the CATCH block is executed immediately. If the CATCH block is exited using the statement RESUME in a resumable exception, the program resumes after the place where the exception was raised. In all other cases, the system deletes the context in which the exception was raised, after the CATCH block is exited.

If the CATCH block is not exited using a statement such as RESUME, RETRY, or any other processing block exits, processing continues after the TRY control structure of the block.

◈ If no handlers are found in any of the participating TRY control structures of a protected area, or if the exception is not raised during processing of a TRY block of a TRY control structure, a runtime error occurs at the point where the exception was raised. The short dump of the runtime error contains the name of the exception class and the exception text.

Note the following special features:

◈ If the user exits the procedure context during the handler search, the interface of the procedure is checked. Only exceptions declared there can be propagated from the procedure. Exceptions of the categories CX_STATIC_CHECK and CX_DYNAMIC_CHECK must be declared explicitly, while exceptions of category CX_NO_CHECK are always declared implicitly. If the exception is not declared in the interface, the exception of the predefined class CX_SY_NO_HANDLER is raised at the call position of the procedure, in whose attribute PREVIOUS a reference to the original exception is stored. This is done at the call position of the procedure,

◈ If a handler is found, the CLEANUP blocks of all TRY control structures that have thus far been scanned unsuccessfully are executed from the inside to the outside, directly before their context is deleted. This means that, if BEFORE UNWIND is declared for the CATCH block, the CLEANUP blocks are executed when the handler is exited; otherwise they are executed before being handled. Exceptions that are raised within a CLEANUP block cannot exit the block; they must be handled there.

No CLEANUP blocks are executed in the following cases:

◈ When the handling of a resumable exception is exited with RESUME.
◈ TRY control structures whose exception is raised in a CATCH block (a CATCH block is not part of the protected range).

⟾ Class-Based Exceptions in Procedures

If a class-based exception is not handled in a procedure, the system attempts to propagate it to the caller of the procedure. The exceptions that can be propagated from a procedure must be declared in its interface. The caller then knows which exceptions to expect from the procedure. Class-based exceptions are divided into three categories, which determine whether the declaration must be explicit and how it is checked.

In methods of local classes and subroutines, the addition RAISING of the statements METHODS and FORM is used for the declaration. In function modules and methods of global classes, class-based exceptions are declared in the interface by selecting the Exception Class checkbox on the relevant tab page in Class Builder or Function Builder. The addition RAISING then appears as a comment of the statement FUNCTION. The declaration of an exception class in an interface is polymorphous. It declares all subclasses simultaneously.

Declarations using RAISING can be used to define whether a resumable exception propagated from a procedure remains resumable or not. A resumable exception remains resumable only if the addition RESUMABLE is declared for every parameter interface it passes when propagated. The addition RESUMABLE in RAISING does not make a non-resumable exception resumable when it is propagated through an interface.

Undeclared exceptions cannot leave a procedure and violate the interface if they are not handled within the procedure. A violation of the interface raises an exception of the predefined class CX_SY_NO_HANDLER, whose exception object contains a reference to the original exception in the attribute PREVIOUS.

The top level of a program into which the exceptions can be propagated are processing blocks without local data area, that is event blocks or dialog modules. Here, all exceptions raised there must be handled; otherwise a runtime error occurs.

When exceptions are propagated from procedures, the following restrictions apply:

◈ Exceptions cannot be declared in the definition of a static constructor. This means that exceptions cannot leave the static constructor. It is not normally possible to tell whether the consumer of a class is the first consumer and whether or not this consumer needs to handle exceptions propagated by the static constructor.
◈ Exceptions cannot be declared in the definition of an event handler. This means that no exceptions can be propagated from an event handler (except those of category CX_NO_CHECK). See Class-Based Exceptions in Event Handlers.

Programs called using SUBMIT ... AND RETURN or CALL TRANSACTION cannot propagate exceptions to the caller, since exception objects are bound to the internal session of a program.

Notes

◈ When an exception of type CX_SY_NO_HANDLER is raised, this indicates a programming error within a procedure, where the programmer forgot to prevent an exception of category CX_DYNAMIC_CHECK or CX_STATIC_CHECK, handle it locally, or declare it. When exceptions of type CX_SY_NO_HANDLER are handled, it is therefore best not to try to handle the original exception and inform the person responsible for the program instead. In the case of exceptions of type CX_STATIC_CHECK, the syntax check also reports this.
◈ If the exception CX_SY_NO_HANDLER is not handled after the interface is violated, the runtime error is raised by the original exception and the associated short dump describes this exception. This indicates that the original exception is to be stopped, handled, or declared primarily by the procedure. The short dump is not intended to specify that the exception CX_SY_NO_HANDLER is handled instead.

⟾ Class-Based Exceptions in Event Handlers

Class-based exceptions in event handlers are a special case of class-based exceptions in procedures.

Since event handlers and triggers are completely detached, the trigger of an event does not know the handler and therefore cannot handle its exceptions. For this reason, no class-based exceptions can be declared using RAISING in the declaration.

This has the following consequences:

◈ If exceptions from the CX_STATIC_CHECK or CX_DYNAMIC_CHECK classes occur during event handling, they must be handled in the event handler or they lead to a violation of the interface, whereby for normal procedures the CX_SY_NO_HANDLER exception is triggered.
◈ If a violation of the interface occurs during event handling, event handling is terminated and the control is given back to the trigger of the event. Further event handlers which are still registered for the event are not executed.
◈ The trigger of the event can handle the CX_SY_NO_HANDLER exception.

Notes

◈ An event handler must handle exceptions of the classes CX_STATIC_CHECK and CX_DYNAMIC_CHECK that are raised during processing. If they are not handled and this leads to a violation of the interface, it is seen as a programming error in the event handling.
◈ The trigger of an event does not usually have to handle exceptions, unless an exception to the class CX_NO_CHECK is expected. The handling of CX_SY_NO_HANDLER is only considered when trying to prevent a possible programming error in an event handler from producing a runtime error.
◈ The trigger of an event should not attempt, any more than the caller of a procedure, to handle the original exception after detecting CX_SY_NO_HANDLER. Unlike for the direct method call, it is not yet determined here which method the event has handled.

3.1.3 RAISE EXCEPTION

Syntax

RAISE [RESUMABLE] EXCEPTION
  { {TYPE cx_class [ message] [EXPORTING p1 = a1 p2 = a2 ...]}
  | oref }.

Addition:

... RESUMABLE

Effect

This statement interrupts execution of the current statement block and raises a class-based exception. It can be used at any point in a processing block. The statement interrupts the program flow and searches for a handler as described in System Response After a Class-Based Exception. Depending on the definition of the handler, the context of the exception is closed before or after the handler is executed; this can include cleanup tasks. Only if the addition RESUMABLE is specified can processing be resumed again after the statement RAISE EXCEPTION during handling, without closing the context.

◈ If the addition TYPE is specified, an exception of exception class cx_class is raised and, if necessary, an exception object is created. Every exception class cx_class visible at this point can be specified after TYPE. The addition EXPORTING can be used to assign appropriate actual parameters to the input parameters of the instance constructor of the exception class by using the same syntax as used for CREATE OBJECT. As in regular method calls, a1, a2, ... are general expression positions. In other words, functions and expressions can be passed as actual parameters, alongside data objects. Special rules apply in this case. The addition message can be used to associate the exception object with a message.

◈ If oref is specified, no new exception object is created when the exception is raised. For oref, an object reference variable must be specified that references an existing exception object. In the existing exception object, the internal attributes that describe the position of the exception and that are read using the method GET_SOURCE_POSITION, are applied at the position of the statement RAISE. In addition, the attribute IS_RESUMABLE is set to a new value, depending on how the addition RESUMABLE is used.

The statement RAISE EXCEPTION must not be used in a method or function module in whose interface non-class-based exceptions are declared. Also, the statement does not permit simultaneous use of the statement CATCHSYSTEM-EXCEPTIONS for the obsolete handling of catchable runtime errors, and the statements RAISE or MESSAGE RAISING to raise non-class-based exceptions in function modules and methods in the current processing block.

Notes

◈ If the addition TYPE is used, an exception object is only created when required, for performance reasons, that is, when an appropriate CATCH or CLEANUP block with the addition INTO is used in a surrounding TRY control structure. In principle, an exception is the same thing as an exception object being generated. A difference in behavior can occur only if a non-handled exception of the instance constructor replaces the original exception when the object is generated. However, this situation should never arise.

◈ If oref is specified, either an exception object instantiated using CREATE OBJECT can be used, or an exception that was previously caught during exception handling can be raised again.

◈ If a caught exception is raised again, note that the exception object does not remain unmodified and that the information about the position of the exception is changed. If the original information is to be propagated to an external handler, a new exception from the same class can be raised. The original exception object is then passed to the parameter PREVIOUS of the constructor of this class. It may be enough to propagate the original exception implicitly (and not raise it again using RAISE). The corresponding original exception can then be evaluated in the CLEANUP block, if required

◈ If a procedure is exited by raising an exception, the content of the formal parameter for which the pass by value is defined is not assigned to the respective actual parameters.

◈ Only the constants of the exception class that specify a static exception text of the exception class should be passed to the TEXTID input parameters of the instance constructor of the exception class. The addition MESSAGE is used to associate any messages with an exception. This addition cannot be specified with the parameterTEXTID.

◈ The addition THROW in a conditional expression makes it possible to raise a class-based exception in an operand position.

Example

A predefined exception is raised explicitly for which an exception text other than the standard exception text is selected and whose placeholder &TOKEN& is filled by passing a value to the attribute with the same name.

DATA exc  TYPE REF TO cx_sy_dynamic_osql_semantics.

TRY.
    ...
    RAISE EXCEPTION TYPE cx_sy_dynamic_osql_semantics
      EXPORTING textid = cx_sy_dynamic_osql_semantics=>unknown_table_name
                token  = 'Test'.
    ...
  CATCH cx_sy_dynamic_osql_semantics INTO exc.
    MESSAGE exc->get_text( ) TYPE 'I'.
ENDTRY.

Addition

... RESUMABLE

Effect

The addition RESUMABLE raises an exception as a resumable exception. When an exception of this type is handled in a CATCH block, the statement RESUME can be used to jump back to directly before the raising statement, as long as the context of the exception was not deleted before the exception was handled.

Notes

◈ If the statement RESUMABLE is used to raise an exception as a resumable exception, the handler has to determine whether processing is resumed after RAISE EXCEPTION, or whether processing for the current context is canceled completely. Both alternatives can occur when an exception is raised. It is important to note that CLEANUP blocks are only executed when the context is deleted.

◈ When exceptions of the types CX_STATIC_CHECK and CX_DYNAMIC_CHECK (which are raised as resumable) are propagated, they can become non-resumable if the addition RESUMABLE is not specified for the addition RAISING (for declaring the exception) in each interface involved.

◈ When exceptions of type CX_NO_CHECK are propagated, the resumable attribute is always retained. However, caution should be used when raising exceptions of type CX_NO_CHECK as resumable, and it is important to ensure a procedure always displays the required behavior.

◐ RAISE EXCEPTION - message

Syntax

... MESSAGE tn(id)
          | { ID mid TYPE mtype NUMBER num }
            [WITH dobj1 ... dobj4] ...

Effect

The addition MESSAGE of the statement RAISE EXCEPTION and of the addition THROW in a conditional expression passes the specification of a message to the exception object. The addition tn(id) or ID mid TYPE mtype NUMBER num is used to specify the message type, the message class, and the message number for the table T100, statically or dynamically. This is done in the same way as in the statement MESSAGE. Also like in MESSAGE, the optional addition WITH can be used to fill the placeholders of a message. The exception class of the raised exception must include one of the system interfaces for messages:

◈ Using IF_T100_DYN_MSG
◈ Using IF_T100_MESSAGE

The attributes of the system interface are used to associate the exception object with the message specified after MESSAGE. Using an object reference, the object can be used directly in the variant MESSAGE oref of the MESSAGE statement and the message texts can be read using the interface methods.

If the addition MESSAGE is used, the input parameter TEXTID of the constructor of the exception class must not be filled. This parameter is only designed for specifying a predefined exception text.

Notes

◈ The addition MESSAGE cannot be specified after the variant RAISE EXCEPTION oref.

Using IF_T100_DYN_MSG

The full functions of the addition MESSAGE are available only if the system interface IF_T100_DYN_MSG is included in the exception class in question. This interface covers the system interface IF_T100_MESSAGE requested by the syntax check. When an exception class is used with the system interface IF_T100_DYN_MSG, the addition MESSAGE does the following:

◈ The message type specified after MESSAGE is assigned to the attribute MSGTY of the interface IF_T100_DYN_MSG.

◈ The components of the structured attribute T100KEY of the component interface IF_T100_MESSAGE are filled as follows:

◈ MSGID is assigned the message class specified after MESSAGE
◈ MSGNO is assigned the message number specified after MESSAGE
◈ ATTR1 to ATTR4 are assigned the names IF_T100_DYN_MSG~MSGV1 to IF_T100_DYN_MSG~MSGV4 of the interface attributes for the placeholders of the message

◈ The attributes MSGV1 to MSGV4 of the interface IF_T100_DYN_MSG are assigned the texts specified using WITH.

Notes

◈ If IF_T100_DYN_MSG is used, the addition MESSAGE assigns the correct values to all required attributes of the exception class to associate them with the message. More specifically, the attributes for the placeholders of the message are mapped to the structure T100KEY of the interface IF_T100_MESSAGE automatically.

◈ The addition MESSAGE fills the attributes of the exception class after the instance constructor is executed. This overwrites any values assigned to the attributes when the exception object was constructed.

Using IF_T100_MESSAGE


If only the system interface IF_T100_MESSAGE is included, only restricted functions are available. When an exception class is used with the system interface IF_T100_DYN_MSG, the addition MESSAGE does the following:

◈ If specified, message types are ignored.

◈ The components of the structured attribute T100KEY of the component interface IF_T100_MESSAGE are filled as follows:

◈ MSGID is assigned the message class specified after MESSAGE
◈ MSGNO is assigned the message number specified after MESSAGE
◈ ATTR1 to ATTR4 are assigned the names of the first four input parameters of the instance constructors specified in the order given.

If only the system interface IF_T100_MESSAGE is included, the addition WITH must not be used.

Notes

◈ It is recommended that the system interface IF_T100_DYN_MSG is used for exceptions raised using the addition MESSAGE.

◈ The use of the addition MESSAGE for an exception class with IF_T100_MESSAGE is designed for cases where existing exception classes that cannot be switched to IF_T100_DYN_MSG need to be used.

◈ The addition MESSAGE is suitable for exising exception classes with IF_T100_MESSAGE that already have a generic exception text without semantic meaning. Until now, structures of the type SCX_T100KEY, filled in the program, had to be passed to exceptions of this type.

◈ The use of the addition MESSAGE is not recommended for an exception class with IF_T100_MESSAGE whose exception texts all have a fixed semantic meaning.

3.1.4 TRY

Syntax

TRY.
    [try_block]
  [CATCH [BEFORE UNWIND] cx_class1 cx_class2 ... [INTO oref].
    [catch_block]]
    ...
  [CLEANUP [INTO oref].
    [cleanup_block]]
ENDTRY.

Effect

The statement TRY introduces a control structure with multiple statement blocks. The first statement block try_block is always executed, whereas a branching off to exactly one of the remaining statement blocks only occurs if a class-based exception is raised in try_block.

A TRY control structure defines the following statement blocks:

◈ A TRY block try_block directly after the statement TRY. The TRY block defines a protected area whose class-based exceptions can be handled in the subsequent CATCH blocks. If no exception is raised in the TRY block and it reaches its end, the system resumes processing after ENDTRY. If a class-based exception is raised in the TRY block, the system searches for an exception handler in the same TRY control structure or in an external structure (see System Behavior).

◈ One or more optional CATCH blocks catch_block for handling exceptions, each directly after a CATCH statement. If the end of a CATCH block is reached without it being left early, the processing continues after the ENDTRY.

◈ An optional CLEANUP block cleanup_block for cleanups directly after the statement CLEANUP.

A TRY control structure invalidates the simultaneous use of the obsolete statement CATCH SYSTEM-EXCEPTIONS to handle catchable runtime errors in the current processing block.

Notes

◈ All statement blocks of a TRY control structure can contain any kind of control structures, in particular further TRY control structures.

◈ No exceptions (except those in category CX_NO_CHECK from event handlers) can be propagated from the static constructors and event handlers, which means they must always be handled locally.

◆ CATCH

Syntax

CATCH [BEFORE UNWIND] cx_class1 cx_class2 ... [INTO oref].

Additions

1. ... BEFORE UNWIND

2. ... INTO oref

Effect

Introduction of a CATCH block of a TRY control structure in which exceptions can be handled.

A CATCH block is an exception handler, meaning the program logic that is executed whenever the associated exception is raised in the TRY block of the same TRY control structure.

A CATCH block handles the exceptions of the exception classes cx_class1 cx_class2 ... that are specified after the statement CATCH as well as the exceptions of the subclasses of these exception classes. In each CATCH statement of a TRY control structure, any number of exception classes cx_class1 cx_class2 ... can be specified, with more specific exception classes (subclasses) being specified before more general exception classes (superclasses). This order must be kept both within a CATCH statement and across multiple CATCH statements of a TRY control structure.

Note

The rule where special exception classes must be specified before general classes in CATCH ensures that an exception is not handled by a general exception handler (superclass) if a special handler (subclass) is provided.

Addition 1

... BEFORE UNWIND

Effect

If the addition BEFORE UNWIND is specified, the context in which the exception was raised is not deleted until the CATCH block is deleted. Instead, the context is preserved (including all called procedures and their local data) during the execution of the CATCH block.

◈ If no RESUME statement is executed in the CATCH block, the context is deleted when the CATCH block is exited.

◈ Of a RESUME statement is executed in the CATCH block, processing resumes after the statement that raised the exception.

If the addition is not specified, the context is deleted before the CATCH block is executed.

Notes

◈ The statement RESUME can be used only when handling a resumable exception and only in a CATCH block in which the addition BEFORE UNWIND is specified. This is the only case in which the context of the exception is not deleted when the CATCH block is exited.

◈ Resumable exceptions can also be handled in CATCH blocks without the addition BEFORE UNWIND. In this case, the context of the exception is deleted before the handling process and the statement RESUME cannot be specified.

◈ Any CLEANUP blocks are always executed directly before their context is deleted. If BEFORE UNWIND is used, after exception handling, and in all other cases before the exception handling.

◈ Use of the addition BEFORE UNWIND for CATCH is only required when the statement RESUME is used. However, it is allowed in principle during exception handling if the context of the exception is to be evaluated before any cleanup activities in CLEANUP blocks. This makes sense, for example, when handling resource bottlenecks if releasing resources in CLEANUP blocks would change the context and thus make the calculation of the free resources in the exception handler meaningless. Other than for logging purposes, it is not advisable to evaluate that part of the context used only locally to implement the procedure with the error.

Addition 2

... INTO oref

Effect

If the addition INTO is specified, a reference to the exception object is saved to oref. The following can be specified for oref:

◈ An existing object reference variable oref, whose static type must be more general or as general as the most general of the specified exception classes.

◈ An inline declaration with DATA(var). The static type of the declared object reference variable is the exception class (if specified). If multiple exception classes are specified and a common superclass of these classes is used, the superclass is the static type of oref; if not, it is CX_ROOT.

The object reference variable can be used to access the attributes and methods of the exception object.

Example

Catches exceptions with an inline declaration of an object reference variable. The static type of this variable is cx.

CLASS cx DEFINITION INHERITING FROM cx_dynamic_check.
  PUBLIC SECTION.
ENDCLASS.

CLASS cy DEFINITION INHERITING FROM cx.
  PUBLIC SECTION.
ENDCLASS.

TRY.
    ...
  CATCH cy cx  INTO DATA(oref).
ENDTRY.

◆ RESUME

Syntax

RESUME.

Effect

This statement exits the CATCH handling of a resumable exception and resumes processing after the statement that raised the exception. This statement can only be executed in a CATCH block of a TRY control structure for which the addition BEFORE UNWIND is declared. When exception handling is exited using RESUME, the context of the exception is not deleted and any CLEANUP blocks are not executed.

The following are prerequisites for resuming processing:

◈ The exception was raised as a resumable exception by the addition RESUMABLE of the statement RAISE EXCEPTION or by the addition THROW in a conditional expression.

◈ The exception was declared with the addition RESUMABLE of RAISING in the interface of all procedures from which it was propagated (if it is not an exception derived from CX_NO_CHECK).

If these points do not apply, an exception of the class CX_SY_ILLEGAL_HANDLER is raised. To check whether the prerequisites are met, the instance attribute IS_RESUMABLE with type abap_bool of the current exception object can be checked. The value of the attribute is set using the addition INTO when the statements CATCH and CLEANUP are executed and when an exception object is reused using the statement RAISE.

Notes

◈ If the resumable exception was not raised by the variant RAISE RESUMABLE EXCEPTION oref, the handler can send information to the raiser of the event by assigning values to appropriate attributes of the exception object.

◈ Resuming processing after an exception is particularly useful if the exception was raised when a special method was called, such as a constructor or a functional method in an operand position.

◈ If a CATCH block is not exited using RESUME when handling a resumable exception, the program does not continue in the context of the statement that raised the exception (as described in System Response After a Class-Based Exception). This context is deleted when the CATCH block is exited, at the latest.

Exceptions

Handleable Exceptions

CX_SY_ILLEGAL_HANDLER

◈ Cause: Exception cannot be resumed. The ID of the exception text is NOT_RESUMABLE
Runtime Error: UNCAUGHT_EXCEPTION

◆ RETRY

Syntax

RETRY.

Effect

This statement exits the CATCH handling of a class-based exception and continues processing with the TRY statement of the current TRY control structure.

The RETRY statement can only be executed in a CATCH block of a TRY control structure.

Notes

◈ The RETRY statement enables a TRY block that raised an exception to be executed again right from the start.

◈ If the BEFORE UNWIND addition is declared for a CATCH block, exiting using RETRY deletes the context of the exception and creates it again in the TRY block. With respect to the context, therefore, RETRY responds like any exit of a CATCH block (with the exception of RESUME.

◈ The cause of the exception must be removed either before RETRY in the CATCH block or after RETRY in the TRY block. If a TRY block is repeated and the cause of the exception is not removed, an endless loop results.

Example

The following exception handling expands the ABAP-specific handling of a division by zero to dividends not equal to zero.

PARAMETERS: number1 TYPE i,
            number2 TYPE i.

DATA result  TYPE p DECIMALS 2.

TRY.
    result = number1 / number2.
    MESSAGE |Result: { result ALIGN = LEFT }| TYPE 'I'.
  CATCH cx_sy_zerodivide.
    number1 = 0.
    RETRY.
ENDTRY.

◆ CLEANUP

Syntax

CLEANUP [INTO oref].

Addition:

... INTO oref

Effect

Introduces a statement block of a TRY control structure where cleanups can be performed.

A CLEANUP block is executed when a class-based exception in the TRY block of the same TRY control structure is raised, but is handled in a CATCH block of an external TRY control structure. A CLEANUP block is executed immediately before the context of the exception is deleted:

◈ If the addition BEFORE UNWIND is specified for the handling CATCH block, the context is deleted when the CATCH block is exited and the CLEANUP block is executed accordingly after handling.

◈ If the addition BEFORE UNWIND is not specified, the context is deleted before the CATCH block is executed and the CLEANUP block is executed accordingly.

◈ If RESUME is used to resume processing after a resumable exception, the context is not deleted and accordingly no CLEANUP block is executed.

The CLEANUP block must be executed completely and must be exited using ENDTRY so that the exception can be propagated to its handler. If an attempt is made to exit the context of a CLEANUP block prematurely, a runtime error occurs. A CLEANUP block cannot contain any statements where the system can detect (statically) that it cannot return to the CLEANUP block. Program calls using SUBMIT and CALL TRANSACTION should also be avoided here.

Notes

◈ The context of the TRY block can be cleaned up in a CLEANUP block. For example, objects can be updated to a consistent state or external resources released to which an external handler would no longer have access.

◈ Since a CLEANUP block must always be executed completely, all the exceptions raised there must also be handled there.

Addition

... INTO oref

Effect

If the addition INTO is specified, a reference to the exception object is saved to oref. The following can be specified for oref:

◈ An existing object reference variable of the type CX_ROOT

◈ An inline declaration DATA(var), where an object reference variable of the type CX_ROOT is declared.

oref can be used to access the exception object.

Note

Within the CLEANUP block, do not raise the current exception again using RAISE EXCEPTION oref, since this would modify the attributes of the exception object.

◆ ENDTRY

Syntax

ENDTRY.

Effect

The statement ENDTRY closes a TRY control structure introduced using TRY.

3.1.4 Examples of Exceptions

◈ Exceptions, RAISE 

This example demonstrates the statement RAISE EXCEPTION.

Source Code

REPORT demo_raise_exception.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA: oref    TYPE REF TO cx_demo_constructor,
          text     TYPE string,
          position TYPE i.
    TRY.
        TRY.
            RAISE EXCEPTION TYPE cx_demo_constructor
              EXPORTING
                my_text = sy-repid.
          CATCH cx_demo_constructor INTO oref.
            text = oref->get_text( ).
            oref->get_source_position(
              IMPORTING  source_line  = position ).
            cl_demo_output=>WRITE_text( |{ position } { text }| ).
            RAISE EXCEPTION oref.
        ENDTRY.
      CATCH cx_demo_constructor INTO oref.
        text = oref->get_text( ).
        oref->get_source_position(
          IMPORTING source_line  = position ).
        cl_demo_output=>WRITE_text( |{ position } { text }| ).
    ENDTRY.
    cl_demo_output=>display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

This example shows the two variants of the RAISE EXCEPTION statement. The first statement raises an exception of class CX_DEMO_CONSTRUCTOR in the inner TRY block, generates the relevant object, and passes the program name to the instance constructor. The inner CATCH block handles the exception, provides the exception text, and raises the exception again without generating a new object. The outer CATCH block handles the exception again. The CX_DEMO_CONSTRUCTOR class is defined in such a way that the passed program name appears in the exception text. The generated instance constructor takes care of this.

The line number in which the exception was raised is shown to indicate that, when the existing exception object was reused, information relevant to this object was modified.

◈ Exceptions, TRY 

This example demonstrates the TRYcontrol structure.

Source Code

REPORT demo_try.

CLASS try_demo DEFINITION.
  PUBLIC SECTION.
    CLASS-DATA: result TYPE p LENGTH 8 DECIMALS 2,
                oref   TYPE REF TO cx_root,
                text   TYPE string.
    CLASS-METHODS main.
  PRIVATE SECTION.
    CLASS-DATA number TYPE i.
    CLASS-DATA out TYPE REF TO if_demo_output.
    CLASS-METHODS calculation
      IMPORTING  p_number LIKE number
      CHANGING p_result LIKE result
               p_text   LIKE text
      RAISING  cx_sy_arithmetic_error.
ENDCLASS.

CLASS try_demo IMPLEMENTATION.
  METHOD main.
    cl_demo_input=>request( CHANGING field = number ).
    out = cl_demo_output=>new( ).
    TRY.
        IF abs( number ) > 100.
          RAISE EXCEPTION TYPE cx_demo_abs_too_large.
        ENDIF.
        calculation( EXPORTING p_number = number
                     CHANGING  p_result = result
                               p_text   = text ).
      CATCH cx_sy_arithmetic_error INTO oref.
        text = oref->get_text( ).
      CATCH cx_root INTO oref.
        text = oref->get_text( ).
    ENDTRY.
    IF NOT text IS INITIAL.
      out->write( text ).
    ENDIF.
    out->display( |Final result: { result ALIGN = LEFT }| ).
  ENDMETHOD.
  METHOD calculation.
    DATA l_oref TYPE REF TO cx_root.
    TRY.
        p_result =  1 / p_number.
        out->write(
          |Result of division: { p_result ALIGN = LEFT }| ).
        p_result = sqrt( p_number ).
        out->write(
          |Result of square root: { p_result ALIGN = LEFT }| ).
      CATCH cx_sy_zerodivide INTO l_oref.
        p_text = l_oref->get_text( ).
      CLEANUP.
        CLEAR p_result.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  try_demo=>main( ).

Description

◈ If the content of number is larger than 100, in the TRY block the TRY control structure of the framework program will trigger an exception of the class CX_DEMO_ABS_TOO_LARGE. This exception is handled by the second CATCH block of the same TRY control structure since the subclass of the most general exception is CX_ROOT.

◈ If the content of number is 0, the runtime environment in the TRY block of the TRY control structure of the called method calculation triggers an exception of the predfined class CX_SY_ZERODIVIDE. This is handled in the CATCH block of the same TRY control structure.

◈ If the content of number is a negative number, the runtime environment in the TRY block of the TRY control structure of the called method calculation triggers an exception of the predefined class CX_SY_ARG_OUT_OF_DOMAIN. Since in this TRY control structure there is no handler defined for this exception, the exception is propagated from the method - which is possible through the declaration of the superclass CX_SY_ARITHMETIC_ERROR with RAISING in the method interface. Beforehand, the CLEANUP block of the inner TRY control structure is executed.

◈ Other possible exceptions are handled in the last CATCH block of the TRY control structure of the framework program. This block catches all possible exceptions through the specification of the most general exception class CX_ROOT. If, for example, CX_SY_ARG_OUT_OF_DOMAIN or one of its superclasses is not declared in the method interface, you would have the exception CX_SY_NO_HANDLER, which would be handled in the last CATCH block.

◈ Exceptions - CATCH 

The example demonstrates the catching of class based exceptions.

Source Code

REPORT demo_catch_exception.

PARAMETERS resumble AS CHECKBOX.
SELECTION-SCREEN ULINE.
PARAMETERS: aft_unw  RADIOBUTTON GROUP rad,
            bef_unw  RADIOBUTTON GROUP rad.
SELECTION-SCREEN ULINE.
PARAMETERS resume AS CHECKBOX.

CLASS lcx_exception DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.

CLASS exc_demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS: main,
                   meth1 RAISING lcx_exception,
                   meth2 RAISING RESUMABLE(lcx_exception).
ENDCLASS.

FIELD-SYMBOLS <fs> TYPE ANY.

CLASS exc_demo IMPLEMENTATION.
  METHOD main.
    DATA exc TYPE REF TO lcx_exception.
    IF aft_unw = 'X'.
      TRY.
          WRITE / 'Trying method call'.
          IF resumble = ' '.
            exc_demo=>meth1( ).
          ELSEIF resumble = 'X'.
            exc_demo=>meth2( ).
          ENDIF.
        CATCH lcx_exception.
          IF <fs> IS ASSIGNED.
            WRITE / 'Context of method available'.
          ELSE.
            WRITE / 'Context of method not available'.
          ENDIF.
      ENDTRY.
      WRITE / 'Continue after main TRY block'.
    ELSEIF bef_unw = 'X'.
      TRY.
          WRITE / 'Trying method call'.
          IF resumble = ' '.
            exc_demo=>meth1( ).
          ELSEIF resumble = 'X'.
            exc_demo=>meth2( ).
          ENDIF.
        CATCH BEFORE UNWIND lcx_exception INTO exc.
          IF <fs> IS ASSIGNED.
            WRITE / 'Context of method available'.
          ELSE.
            WRITE / 'Context of method not available'.
          ENDIF.
          IF resume = 'X'.
            IF exc->is_resumable = 'X'.
              RESUME.
            ELSE.
              WRITE / 'Resumption not possible'.
            ENDIF.
          ENDIF.
      ENDTRY.
      WRITE / 'Continue after main TRY block'.
    ENDIF.
  ENDMETHOD.
  METHOD meth1.
    DATA loc TYPE i.
    ASSIGN loc TO <fs>.
    TRY.
        WRITE / 'Raising non-resumable exception'.
        RAISE EXCEPTION TYPE lcx_exception.
        WRITE / 'Never executed'.
      CLEANUP.
        WRITE / 'Cleanup in method'.
    ENDTRY.
    WRITE / 'Continue after TRY block in method'.
  ENDMETHOD.
  METHOD meth2.
    DATA loc TYPE i.
    ASSIGN loc TO <fs>.
    TRY.
        WRITE / 'Raising resumable exception'.
        RAISE RESUMABLE EXCEPTION TYPE lcx_exception.
        WRITE / 'Resuming method'.
      CLEANUP.
        WRITE / 'Cleanup in method'.
    ENDTRY.
    WRITE / 'Continue after TRY block in method'.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  exc_demo=>main( ).

Description

The meth1 method raises a non-resumable exception, the meth2 method raises a resumable exception that is handled in the TRY-control structure of the method main using CATCH.

◈ If handling takes place without BEFORE UNWIND, the CLEANUP block is executed in both cases before handling and the context of the method called is not available during handling.

◈ If handling takes place with BEFORE UNWIND, the context of the method called is available in both cases during handling and the CLEANUP block is executed after the handling.

◈ When a resumable exception is raised, the RESUME statement can be executed during the handling. This statement makes sure that processing in the method called is continued without its CLEANUP block being executed.

3.2 Exceptions Before Class-Based Exceptions

Exceptions are class-based. Before the introduction of exception classes, there were the following types of exceptions:

◈ Exception situations detected by the system whose causes could be handled effectively by the program, raised catchable runtime errors that could be handled by the statement CATCH SYSTEM-EXCEPTIONS. Catchable runtime errors are obsolete. An exception class is assigned to each catchable runtime error, to be handled in a TRY block. Conversely, not all predefined, class-based exceptions are assigned to a catchable runtime error. No new catchable runtime errors are created, and existing runtime errors that should be made ready for handling are no longer converted to catchable runtime errors. Instead, exception classes are assigned to them.

◈ Self-defined handleable exceptions were allowed only in the interfaces of function modules and methods. These types of exceptions can be raised within the procedure by the statement RAISE or MESSAGE RAISING as soon as an exception situation is recognized. The procedure caller can use the addition EXCEPTIONS of the statement CALL FUNCTION or meth( ... ) to assign return codes for the system field sy-subrc to the exceptions the caller wants to handle and evaluate them after the call. However, this is not true exception handling in the sense of reacting to an event.


Interaction of the Exception Concepts


The class-based exceptions replace and enhance the previous concepts in full. Class-based exceptions can be raised and handled in all ABAP contexts (programs, processing blocks). In particular, all previously catchable runtime errors can be handled as class-based exceptions, with the previous exception groups being mapped to shared superclasses. For reasons of downward-compatibility, the catchable runtime errors and the non-class-based exceptions defined in the interfaces of methods and function modules have not been abolished, but their use has been restricted as follows:

◈ Within a processing block, only one kind of exception can be raised.
◈ Within a procedure interface, the declaration of class-based exceptions and the definition of non-class-based exceptions exclude one another.
◈ Within a processing block, catchable runtime errors cannot be caught using CATCH SYSTEM-EXCEPTIONS if class-based exceptions are handled there in TRY blocks or are raised using RAISE EXCEPTION or the addition THROW in a conditional expression. Instead, catchable runtime errors must be caught when handling the associated class-based exception.

For reasons of interoperability, class-based exceptions can be handled within a processing block and return codes of function modules and methods evaluated there using non-class-based exceptions.

3.2.1 Non-Class-Based Exceptions

Definition

Non-class-based exceptions can be defined in the interfaces of function modules and methods. These exceptions are defined as follows:

◈ For methods of local classes, by assigning a name for the exception after the addition EXCEPTIONS of the statement [CLASS-]METHODS.

◈ For methods of global classes or function modules, by assigning a name for the exception in Class Builder or Function Builder, in which case the radio button for exception classes is not selected.

The statement RAISE EXCEPTION or the addition THROW in a conditional expression cannot be used to raise class-based exceptions in a method or a function module in whose interface non-class-based exceptions are defined.

Note

Before class-based exceptions were introduced, all exceptions defined in the interface of methods of global classes or function modules were non-class-based.

Raising Exceptions

Non-class-based exceptions are raised by the following statements:

◈ RAISE
◈ MESSAGE ... RAISING

Handling Exceptions

The handling of non-class-based exceptions is made possible by the addition EXCEPTIONS in method calls and in function module calls. Number values are assigned to the exceptions, which are used to fill the system field sy-subrc when the exception is raised. The actual error handling takes place after the call, when the system evaluates sy-subrc.


Notes

- The exceptions that can be defined in the interfaces of methods and function modules are not real exceptions, since they do not modify the control flow; instead they just end the processing of the procedure prematurely and set the return code sy-subrc.

- RFC currently allows only classic exception handling. Class-based exception handling is only possible in a different release track.

◉ RAISE

Syntax

RAISE exception.

Effect

This statement raises the non-class-based exception exception.

◈ RAISE can be specified in a method only if the non-class-based exception is defined in the interface of the method.

◈ Specify RAISE in function modules only if the non-class-based exception is defined in the interface of the function module.

◈ It is possible to specify RAISE in all other positions, but this is not recommended.

After the exception exception is raised, the system proceeds as follows:

◈ If the exception is raised in a method or function module whose caller assigns a return value to the exception, the procedure ends immediately, the system returns to the calling position, and the system field sy-subrc is set according to the assignment.

◈ If the exception is raised in a method or function module whose caller does not assign a return value to the exception, a runtime error is then triggered whose short dump contains the name of the exception.

◈ If the exception is raised in a subroutine, the system searches for the first function module in the procedures of the preceding call stack. If it finds a function module of this type and the exception is defined in it, the system acts as though the exception was raised in this function module. Otherwise, a runtime error occurs.

◈ In all other processing blocks, raising a non-class-based exception produces a runtime error that immediately ends the program.

This form of the statement RAISE cannot be used in the same processing block as the statement RAISE EXCEPTION or the addition THROW in a conditional expression to raise class-based exceptions.

Notes

◈ The statement MESSAGE with the addition RAISING also raises a non-class-based exception. In cases in which non-class-based exceptions are still used, this statement is preferred instead of RAISE, because it offers the option of adding a text to the exception.

◈ If a procedure is exited by raising an exception, the content of the formal parameter for which the pass by value is defined is not assigned to the respective actual parameters.

Exceptions

Non-Handleable Exceptions 

◈ Cause: The raised exception was not handled by the caller.
Runtime Error: RAISE_EXCEPTION

◉ MESSAGE - RAISING

Syntax

MESSAGE { msg | text } [DISPLAY LIKE dtype] [WITH dobj1... dobj4]
        RAISING exception.

Effect

The statement MESSAGE with the addition RAISING raises a non-class-based exception exception and only sends a message if the exception is not handled. The semantics of msg, text, and WITH is the same as in the statement MESSAGE without the addition RAISING.

This addition only makes sense during the processing of methods and function modules in which the non-class-based exception exception is defined. Furthermore, it cannot be used in the same processing block as the statement RAISE EXCEPTION or the addition THROW in a conditional expression to raise class-based exceptions.

◈ If the MESSAGE-statement is processed with the addition RAISING during processing of a method or a function module whose caller assigns a return value to the exception exception with the addition EXCEPTIONS of the statement CALL, it has the same effect as the statement RAISE.

◈ If no return value is assigned to the exception exception, the addition RAISING is ignored and the message is sent using the statement MESSAGE and processed in accordance with its message type.

The system fields of the statement MESSAGE are populated in both cases and are available in the calling program after handling an exception raised with MESSAGE ...RAISING. This is especially true if a function module was called using Remote Function Call (RFC).

Notes

◈ The statement MESSAGE ... RAISING is primarily a statement for raising exceptions and not for sending messages. An exception of this type should always be handled like an exception raised using RAISE, since the behavior of the message depends strongly on the context and is usually unpredictable when the function module is created.

◈ Using MESSAGE ... RAISING in cases in which non-class-based exceptions must still be used is preferable to using the RAISE statement, because it offers the option of providing additional text information with an exception.

◈ A return value can be assigned to messages that are sent in function modules without the addition RAISING by using the predefined exception error_message.

◈ Messages sent as messages when a function module is called and not caught (despite RAISING) are processed as with error_message.

◈ If a procedure is exited by raising an exception, the content of the formal parameter for which the pass by value is defined is not assigned to the respective actual parameters.

Example

When the message is called for the first time, an information message is sent. The second time, an exception is raised instead, which is handled by sy-subrc.

CLASS c1 DEFINITION.
       PUBLIC SECTION.
         CLASS-METHODS m1 EXCEPTIONS exc1.
     ENDCLASS.
     CLASS c1 IMPLEMENTATION.
       METHOD m1.
         MESSAGE 'Message in a Method' TYPE 'I' RAISING exc1.
       ENDMETHOD.
     ENDCLASS.
     ...
       c1=>m1( ).
       c1=>m1( EXCEPTIONS exc1 = 4 ).
     IF sy-subrc = 4.
       ...
     ENDIF.

3.3 Exceptions in ABAP Statements

Error situations that occur during the execution of an ABAP statement raise exceptions. These exceptions are completely integrated into the exception concept and are raised by the runtime environment. Two types of exception exist:

◈ Handleable exceptions, based on predefined exception classes.
◈ Non-handleable exceptions, which produce runtime errors directly.

Each handleable exception is associated with a runtime error. The program terminates with this error if the exception is neither handled nor propagated to a caller. The keyword documentation lists the type of exceptions that can be raised for each statement.

For reasons of backward compatibility, handleable exceptions raised by many ABAP statements can be caught by using both TRY ... ENDTRY and the obsolete statement CATCH SYSTEM-EXCEPTIONS ... ENDCATCH.For this to be possible, the runtime error assigned to the exception class must be catchable. Within processing blocks, the two mechanisms prevent each other from handling exceptions. It is advisable to catch an exception between TRY ... ENDTRY using CATCH or to use the RAISING addition in the definition of the interface to propagate it to the caller. Catching exceptions using CATCH SYSTEM-EXCEPTIONS is no longer recommended.

Example

Unhandled Exception

The following program lines produce the runtime error COMPUTE_INT_ZERODIVIDE because division by zero is invalid and this exception situation is not handled:

DATA result TYPE i.
result = 1 / 0.

Handling Exceptions Using Exception Classes

The above exception is represented by the exception class CX_SY_ZERODIVIDE, which is a subclass of the exception class CX_SY_ARITHMETIC_ERROR. This means that the exception can be handled as follows (the ERR_TEXT variable is passed the text 'Division by zero.'):

DATA myref TYPE REF TO cx_sy_arithmetic_error.
DATA err_text TYPE string.
DATA result TYPE i.
TRY.
    result = 1 / 0.
  CATCH cx_sy_arithmetic_error INTO myref.
    err_text = myref->get_text( ).
ENDTRY.

Handling Exceptions as Catchable Runtime Errors

The runtime error COMPUTE_INT_ZERODIVIDE is catchable and assigned to the exception group ARITHMETIC_ERRORS, which means it can also be handled using the obsolete statement CATCH SYSTEM-EXCEPTIONS.

DATA result TYPE i.
CATCH SYSTEM-EXCEPTIONS arithmetic_errors = 4.
  result = 1 / 0.
ENDCATCH.
IF sy-subrc = 4.
  ...
ENDIF.

3.3.1 Exception Classes for ABAP Statements 

The handleable exceptions in ABAP statements can be caught using the predefined exception classes listed below. These classes are subclasses of the classes CX_DYNAMIC_CHECK and CX_NO_CHECK. These exception classes replace the obsolete catchable runtime errors. The keyword documentation lists the type of exceptions that can be raised for each statement.

To improve the structure, intermediate abstract classes were introduced so that groups of exceptions can be caught simultaneously.

Each exception class includes message texts, which can be displayed by choosing the Texts tab in Class Builder. The following links open Class Builder for the exception class in question.

CX_ROOT
  |
  |--CX_STATIC_CHECK
  |
  |--CX_DYNAMIC_CHECK
  |    |
  |    |--CX_SY_ARITHMETIC_ERROR
  |    |    |
  |    |    |--CX_SY_ZERODIVIDE
  |    |    |
  |    |    |--CX_SY_ARITHMETIC_OVERFLOW
  |    |    |
  |    |    |--CX_SY_ARG_OUT_OF_DOMAIN
  |    |    |
  |    |    |--CX_SY_PRECISION_LOSS
  |    |
  |    |--CX_SY_ASSIGN_ERROR
  |    |    |
  |    |    |--CX_SY_ASSIGN_CAST_ERROR
  |    |    |    |
  |    |    |    |--CX_SY_ASSIGN_CAST_ILLEGAL_CAST
  |    |    |    |
  |    |    |    |--CX_SY_ASSIGN_CAST_UNKNOWN_TYPE
  |    |    |
  |    |    |--CX_SY_ASSIGN_OUT_OF_RANGE
  |    |
  |    |--CX_SY_CODEPAGE_CONVERTER_INIT
  |    |
  |    |--CX_SY_CONVERSION_ERROR
  |    |    |
  |    |    |--CX_SY_CONVERSION_CODEPAGE
  |    |    |
  |    |    |--CX_SY_CONVERSION_CODEPAGE_EX
  |    |    |
  |    |    |--CX_SY_CONVERSION_DATA_LOSS
  |    |    |
  |    |    |--CX_SY_CONVERSION_EXACT_NOT_SUP
  |    |    |
  |    |    |--CX_SY_CONVERSION_INEXACT_FLTP
  |    |    |
  |    |    |--CX_SY_CONVERSION_NO_BOOLEAN
  |    |    |
  |    |    |--CX_SY_CONVERSION_NO_DATE_TIME
  |    |    |
  |    |    |--CX_SY_CONVERSION_NO_NUMBER
  |    |    |
  |    |    |--CX_SY_CONVERSION_NO_QNAME
  |    |    |
  |    |    |--CX_SY_CONVERSION_NO_RAW
  |    |    |
  |    |    |--CX_SY_CONVERSION_NO_TIME
  |    |    |
  |    |    |--CX_SY_CONVERSION_NO_UUID
  |    |    |
  |    |    |--CX_SY_CONVERSION_OVERFLOW
  |    |    |
  |    |    |--CX_SY_CONVERSION_ROUNDING
  |    |    |
  |    |    |--CX_SY_CONVERSION_SRC_TOO_SHORT
  |    |    |
  |    |    |--CX_SY_CONVERSION_UNKNOWN_LANGU
  |    |
  |    |--CX_SY_CREATE_ERROR
  |    |    |
  |    |    |--CX_SY_CREATE_OBJECT_ERROR
  |    |    |
  |    |    |--CX_SY_CREATE_DATA_ERROR
  |    |
  |    |--CX_SY_DATA_ACCESS_ERROR
  |    |    |
  |    |    |--CX_SY_RANGE_OUT_OF_BOUNDS
  |    |    |
  |    |    |--CX_SY_TAB_RANGE_OUT_OF_BOUNDS
  |    |    |
  |    |    |--CX_SY_OFFSET_NOT_ALLOWED
  |    |
  |    |--CX_SY_DB_PROCEDURE_CALL
  |    |    |
  |    |    |--CX_SY_DB_PROCEDURE_CONNECTION
  |    |    |
  |    |    |--CX_SY_DB_PROCEDURE_NOT_FOUND
  |    |    |
  |    |    |--CX_SY_DB_PROCEDURE_NOT_SUPP
  |    |    |
  |    |    |--CX_SY_DB_PROCEDURE_OVERFLOW
  |    |    |
  |    |    |--CX_SY_DB_PROCEDURE_PARAMETER
  |    |         |
  |    |         |--CX_SY_DB_PROCEDURE_DYN_IN_OUT
  |    |         |
  |    |         |--CX_SY_DB_PROCEDURE_DYN_MISSING
  |    |         |
  |    |         |--CX_SY_DB_PROCEDURE_DYN_NOT_FND
  |    |         |
  |    |         |--CX_SY_DB_PROCEDURE_TYPE_ERROR
  |    |
  |    |--CX_SY_DYN_CALL_ERROR
  |    |    |
  |    |    |--CX_SY_DYN_CALL_ILLEGAL_CLASS
  |    |    |
  |    |    |--CX_SY_DYN_CALL_ILLEGAL_FORM
  |    |    |
  |    |    |--CX_SY_DYN_CALL_ILLEGAL_FUNC
  |    |    |
  |    |    |--CX_SY_DYN_CALL_ILLEGAL_METHOD
  |    |    |
  |    |    |--CX_SY_DYN_CALL_PARAMETER_ERROR
  |    |         |
  |    |         |--CX_SY_DYN_CALL_EXCP_NOT_FOUND
  |    |         |
  |    |         |--CX_SY_DYN_CALL_ILLEGAL_TYPE
  |    |         |
  |    |         |--CX_SY_DYN_CALL_PARAM_MISSING
  |    |         |
  |    |         |--CX_SY_DYN_CALL_PARAM_NOT_FOUND
  |    |
  |    |--CX_SY_EXPORT_NO_SHARED_MEMORY
  |    |
  |    |--CX_SY_FILE_ACCESS_ERROR
  |    |    |
  |    |    |--CX_SY_FILE_AUTHORITY
  |    |    |
  |    |    |--CX_SY_FILE_CLOSE
  |    |    |
  |    |    |--CX_SY_FILE_IO
  |    |    |
  |    |    |--CX_SY_FILE_OPEN
  |    |    |
  |    |    |--CX_SY_FILE_OPEN_MODE
  |    |    |
  |    |    |--CX_SY_FILE_POSITION
  |    |    |
  |    |    |--CX_SY_FILE_TRUNCATE
  |    |
  |    |--CX_SY_FIND_INFINITE_LOOP
  |    |
  |    |--CX_SY_GEN_SOURCE_TOO_WIDE
  |    |
  |    |--CX_SY_IMPORT_MISMATCH_ERROR
  |    |    |
  |    |    |--CX_SY_IMPORT_FORMAT_ERROR
  |    |
  |    |--CX_SY_ITAB_ERROR
  |    |    |
  |    |    |--CX_SY_ITAB_DUPLICATE_KEY
  |    |    |
  |    |    |--CX_SY_ITAB_DYN_LOOP
  |    |    |
  |    |    |--CX_SY_ITAB_LINE_NOT_FOUND
  |    |
  |    |--CX_SY_MATCHER
  |    |    |
  |    |    |--CX_SY_REGEX_TOO_COMPLEX
  |    |    |
  |    |    |--CX_SY_INVALID_REGEX_FORMAT
  |    |
  |    |--CX_SY_MOVE_CAST_ERROR
  |    |
  |    |--CX_SY_PROGRAM_NOT_FOUND
  |    |
  |    |--CX_SY_PROVIDE_EXCEPTION
  |    |    |
  |    |    |--CX_SY_PROVIDE_INTERVAL_OVERLAP
  |    |    |
  |    |    |--CX_SY_PROVIDE_TABLE_NOT_SORTED
  |    |
  |    |--CX_SY_READ_SRC_LINE_TOO_LONG
  |    |
  |    |--CX_SY_REF_IS_INITIAL
  |    |
  |    |--CX_SY_REGEX
  |    |    |
  |    |    |--CX_SY_INVALID_REGEX
  |    |
  |    |--CX_SY_REPLACE_INFINITE_LOOP
  |    |
  |    |--CX_SY_SCAN_SOURCE_TOO_WIDE
  |    |
  |    |--CX_SY_SQL_ERROR
  |    |    |
  |    |    |--CX_SY_DB_PROCEDURE
  |    |    |    |
  |    |    |    |--CX_SY_DB_PROCEDURE_SQL_ERROR
  |    |    |
  |    |    |--CX_SY_EXPIMP_DB_SQL_ERROR
  |    |    |
  |    |    |--CX_SY_OPEN_SQL_ERROR
  |    |    |    |
  |    |    |    |--CX_SY_OPEN_SQL_DB
  |    |    |    |
  |    |    |    |--CX_SY_DYNAMIC_OSQL_ERROR
  |    |    |    |    |
  |    |    |    |    |--CX_SY_DYNAMIC_OSQL_SEMANTICS
  |    |    |    |    |
  |    |    |    |    |--CX_SY_DYNAMIC_OSQL_SYNTAX
  |    |    |    |
  |    |    |    |--CX_SY_SQL_UNSUPPORTED_FEATURE
  |    |    |
  |    |    |--CX_SY_NATIVE_SQL_ERROR
  |    |
  |    |--CX_SY_UNKNOWN_CURRENCY
  |    |
  |    |--CX_SY_WRITE_INVALID_STYLE
  |    |
  |    |--CX_SY_WRITE_SRC_LINE_TOO_LONG
  |    |
  |    |--CX_TRANSFORMATION_ERROR
  |         |
  |         |--CX_ST_ERROR
  |         |    |
  |         |    |--CX_ST_CALL_ERROR
  |         |    |
  |         |    |--CX_ST_CALL_METHOD_ERROR
  |         |    |
  |         |    |--CX_ST_CONSTRAINT_ERROR
  |         |    |
  |         |    |--CX_ST_CONDITION
  |         |    |
  |         |    |--CX_ST_DESERIALIZATION_ERROR
  |         |    |
  |         |    |--CX_ST_FORMAT_ERROR
  |         |    |
  |         |    |--CX_ST_INVALID_XML
  |         |    |
  |         |    |--CX_ST_MATCH
  |         |    |
  |         |    |--CX_ST_REF_ACCESS
  |         |    |
  |         |    |--CX_ST_RUNTIME_ERROR
  |         |    |
  |         |    |--CX_ST_SERIALIZATION_ERROR
  |         |
  |         |--CX_XSLT_EXCEPTION
  |              |
  |              |--CX_XSLT_SYSTEM_ERROR
  |                   |
  |                   |--CX_XSLT_ABAP_CALL_ERROR
  |                   |
  |                   |--CX_XSLT_DESERIALIZATION_ERROR
  |                   |
  |                   |--CX_XSLT_FORMAT_ERROR
  |                   |
  |                   |--CX_XSLT_RUNTIME_ERROR
  |                   |
  |                   |--CX_XSLT_SERIALIZATION_ERROR
  |         |
  |         |--CX_SY_TRANS_OPTION_ERROR
  |
  |--CX_NO_CHECK
       |
       |--CX_BADI
       |    |
       |    |--CX_BADI_CONTEXT_ERROR
       |    |
       |    |--CX_BADI_FILTER_ERROR
       |    |
       |    |--CX_BADI_INITIAL_CONTEXT
       |    |
       |    |--CX_BADI_INITIAL_REFERENCE
       |    |
       |    |--CX_BADI_NOT_SINGLE_USE
       |    |    |
       |    |    |--CX_BADI_MULTIPLY_IMPLEMENTED
       |    |    |
       |    |    |--CX_BADI_NOT_IMPLEMENTED
       |    |
       |    |--CX_BADI_UNKNOWN_ERROR
       |
       |--CX_SY_AUTHORIZATION_ERROR
       |
       |--CX_SY_EXPORT_BUFFER_NO_MEMORY
       |
       |--CX_SY_GENERATE_SUBPOOL_FULL
       |
       |--CX_SY_ILLEGAL_HANDLER
       |
       |--CX_SY_LOCALIZATION_ERROR
       |
       |--CX_SY_NESTED_PRINT_ON
       |
       |--CX_SY_NO_HANDLER
       |
       |--CX_SY_PIPES_NOT_SUPPORTED
       |
       |--CX_SY_PIPE_REOPEN
       |
       |--CX_SY_REMOTE_CALL_ERROR
       |    |
       |    |--CX_SY_RMC_COMM_FAILURE
       |    |
       |    |--CX_SY_RMC_INVALID_STATUS
       |    |
       |    |--CX_SY_RMC_SYSTEM_FAILURE
       |
       |--CX_SY_TOO_MANY_FILES

3.4 Runtime Error

Runtime errors identify situations in which the system cannot continue running an ABAP program and has to terminate it. Runtime errors can occur in one of the following situations when ABAP programs are executed:

◈ Unhandled exceptions
    ◈ When a handleable exception is not handled.
    ◈ An unhandleable exception is raised.
◈ An exit message is sent.
◈ An assertion fails.

Each runtime error is identified by a name and assigned to a specific error situation. Each runtime error results in a database rollback. Following a program termination caused by a runtime error, the system displays a short dump which indicates the name of the runtime error, the associated exception class, contents of data objects, active calls, control structures, and so on, and allows the user to navigate directly to the ABAP Debugger. Short dumps are retained in the system for 14 days and managed using the ABAP dump analysis (transaction ST22). You can change the output of the short dump in the profile parameter rdisp/verbose_level if you have special requirements.

Note

If you want to implement a program-controlled termination of a program in situations where it is not a good idea for the program to continue, then we recommend that you use assertions from now on and avoid using exit messages.

4. Conditional Expressions

A conditional expression is a constructor expression that creates a value or raises a class-based exception as specified by a logical expression or a case distinction. Conditional expressions are constructed using the following conditional operators:

◈ COND
◈ SWITCH

4.1 COND - Conditional Operator

Syntax

... COND type( [let_exp]
               WHEN log_exp1 THEN [ let_exp] result1
             [ WHEN log_exp2 THEN [ let_exp] result2 ]
             ...
             [ ELSE [ let_exp] resultn ] ) ...

Effect

A conditional expression with the conditional operator COND has a result, result, that is specified by logical expressions. Either a value with the data type specified by type is produced or a class-based exception raised. The following can be specified for type:

◈ A non-generic data type dtype.
◈ The # character as a symbol for the operand type.
    ◈ If the data type required in an operand position is unique and fully identifiable, this type is used.
    ◈ If the operand type is not fully identifiable, an operand with a statically identifiable type must be specified after the first THEN (except when passing the constructor parameter to an actual parameter with generically typed formal parameter). This type is then used. In particular, THROWs cannot then be specified after THEN.
    ◈ If the constructor expression is passed to an actual parameter with generically typed formal parameter, the operand type is derived in accordance with special rules.
    ◈ In other cases, the character # cannot be specified.

All operands specified after THEN must be convertible to the data type determined by type. In the case of reference variables, an up cast must be possible.

In the parentheses, WHEN must be specified with at least once with any logical expression log_exp. This can be followed by any number of WHENs with further logical expressions. An ELSE can be specified at the end. The expression evaluates the logical expressions one after the other and selects the result specified after THEN of the first logical expression whose result is true. The selected result determines the result of the conditional expression. If none of the logical expressions are true, the result specified after ELSE is selected. If ELSE is not specified, the result is the initial value of the data type type.

A LET expression can be specified before the first WHEN, after every THEN and after ELSE to define local auxiliary fields.

Notes

◈ Rules apply when deriving the type in cases where # is specified for actual parameters that can be passed to generically typed formal parameters. These rules prevent syntax errors in programs that call a procedure and the procedure makes the full typing of a formal parameter type more general by switching to a generic type.

◈ No suitable full type can be derived for formal parameters with the generic types c, n, and x.

Example

Transforms a time to 12 hour format using a conditional expression in an operand position. The type of the result is used by the operand after the first specified THEN, which makes it string.

CLASS cx_cant_be DEFINITION INHERITING FROM cx_no_check.
ENDCLASS.

cl_demo_output=>display(
  COND #( LET t = '120000' IN
          WHEN sy-timlo < t THEN
            |{ sy-timlo TIME = ISO } AM|
          WHEN sy-timlo > t AND sy-timlo < '240000' THEN
            |{ CONV t( sy-timlo - 12 * 3600 ) TIME = ISO } PM|
          WHEN sy-timlo = t THEN
            |High Noon|
          ELSE
            THROW cx_cant_be( ) ) ).

4.2 SWITCH - Conditional Operator

Syntax

... SWITCH type( [let_exp]
                 operand
                 WHEN const1 THEN [ let_exp] result1
               [ WHEN const2 THEN [ let_exp] result2 ]
               ...
               [ ELSE [ let_exp] resultn ] ) ...

Effect

A conditional expression with the conditional operator SWITCH has a result, result, that is specified by a case distinction. Either a value with the data type specified by type is produced or a class-based exception raised. The following can be specified for type:

◈ A non-generic data type dtype.
◈ The # character as a symbol for the operand type.
    ◈ If the data type required in an operand position is unique and fully identifiable, this type is used.
    ◈ If the operand type is not fully identifiable, an operand with a statically identifiable type must be specified after the first THEN (except when passing the constructor parameter to an actual parameter with generically typed formal parameter). This type is then used. In particular, THROWs cannot then be specified after THEN.
    ◈ If the constructor expression is passed to an actual parameter with generically typed formal parameter, the operand type is derived in accordance with special rules.
    ◈ In other cases, the character # cannot be specified.

All operands specified after THEN must be convertible to the data type determined by type. In the case of reference variables, an up cast must be possible.

The position operand in the parenthesis is the value checked in the case distinction. This is a general expression position. It must be followed by at least one WHEN. Literals and constants can be specified for const behind WHEN. It must be possible to compare them with operand. This can be followed by any number of WHENs with further constant values. An ELSE can be specified at the end. This expression compares the values of the operand operand with the specified constant values, one by one, and chooses the result behind THEN for which the values of operand and constant are identical for the first time. The selected result determines the result of the conditional expression. If no matches are found, the result specified after ELSE is selected. If ELSE is not specified, the result is the initial value of the data type type.

If an item specified after THEN or ELSE can be selected, either the result is set or a class-based exception is raised, just as with a conditional expression COND.

A LET expression can be specified in front of the operand operand, after every THEN. and after ELSE to define local auxiliary fields.

Notes

◈ Text symbols for const cannot be specified after WHEN.

◈ A conditional expression with SWITCH has the same meaning as the following conditional expression with COND:

COND type( WHEN operand = const1 THEN result1
         [ WHEN operand = const2 THEN result2 ]
         ...
         [ ELSE resultn ] )

Rules apply when deriving the type in cases where # is specified for actual parameters that can be passed to generically typed formal parameters. These rules prevent syntax errors in programs that call a procedure and the procedure makes the full typing of a formal parameter type more general by switching to a generic type.

◈ No suitable full type can be derived for formal parameters with the generic types c, n, and x.

Example

Conditional operator SWITCH in an operand position in a loop. The loop is exited when the exception after ELSE is caught.

CLASS cx_overflow DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.

DATA(out) = cl_demo_output=>new( ).
DO.
  TRY.
      out->write(
        SWITCH string( sy-index
                       WHEN 1 THEN 'one'
                       WHEN 2 THEN 'two'
                       WHEN 3 THEN 'three'
                       ELSE THROW cx_overflow( ) ) ).
    CATCH cx_overflow.
      out->display( ).
      EXIT.
  ENDTRY.
ENDDO.

4.3 COND, SWITCH - result

Syntax

...   operand
  | { THROW [RESUMABLE] cx_class( [ message]
                                  [p1 = a1 p2 = a2 ...] ) } ...

Alternatives:

1. ... operand

2. ... THROW [RESUMABLE] cx_class( [message] [p1 = a1 p2 = a2 ...] )

Effect

These strings specified after THEN and ELSE in the conditional expressions COND and SWITCH determine the result of the expression if the appropriate branch is selected.

Alternative 1

... operand

Effect

If an operand operand is specified, its value is converted to the data type type, if necessary, and returned as the result of the conditional expression. operand is a general expression position with the following restrictions:

◈ If operand is specified as a string expression, type must be character-like.
◈ If operand is specified as a bit expression, type must be byte-like.

Note

When an operand is selected, the condition operators COND and SWITCH create a temporary data object (from a technical perspective) whose data type is determined by the specified type and whose content is determined by the selected operand. This data object is used as the operand of a statement and then deleted. It is deleted when the current statement is closed or after the analysis of a relational expression once the truth value is determined.

Alternative 2

... THROW [RESUMABLE] cx_class( [message] [p1 = a1 p2 = a2 ...] )

Effect

If specified, THROW raises an exception of the exception class cx_class specified after it in accordance with the rules of the statement RAISE EXCEPTION. Here,

◈ a message can be passed to the output class using the addition MESSAGE
◈ the input parameters p1, p2, ... of the instance constructor can be filled with actual parameters a1, a2.

Notes

◈ If the exception class is specified after THROW, the parentheses must always be specified, even if no messages or actual parameters are passed. EXPORTING cannot be specified, nor does it need to be.
◈ Like the statement RAISE EXCEPTION, THROW cannot be used in a method or function module in whose interface non-class-based exceptions are declared. Also, the statement does not permit simultaneous use of the statement CATCHSYSTEM-EXCEPTIONS for the obsolete handling of catchable runtime errors, and the statements RAISE or MESSAGE RAISING to raise non-class-based exceptions in the current processing block.

Example

Raises an exception of the class CX_DEMO_DYN_T100 that includes the interface IF_T100_DYN_MSG.

DATA cflag TYPE abap_bool.

...

DATA(iflag) = COND i( WHEN cflag = abap_true  THEN 1
                      WHEN cflag = abap_false THEN 0
                      ELSE THROW cx_demo_dyn_t100(
                             MESSAGE e888(sabapdemos)
                                     WITH 'Illegal value!' ) ).

4.4 COND, SWITCH - Type Inference for Actual Parameters

If a constructor expression

COND|SWITCH #( ... THEN ... )

is passed to generically typed formal parameters as an actual parameter using the character # as a symbol for the operand type, the following type inference is performed for the character #:

◈ If the data type of the operand after the first THEN is identifiable statically and matches the generic type of the formal parameter, this data type is used.
◈ If the data type of the operand after the first THEN is identifiable statically and does not match the generic type of the formal parameter or of it is not identifiable statically, the type is derived from the generic type as follows:

◈ string for csequence and clike
◈ xstring for xsequence
◈ decfloat34 for numeric and decfloat
◈ p with the length 8 and no decimal places if p is generic
◈ The standard key for a standard table type with generic primary table key

Other generic data types (except for table types) that are explicitly generic with respect to their secondary table keys cannot be made more concrete and produce a syntax error. More specifically, this applies to the types c, n, and x with generic lengths.

Note

Rules apply when deriving the type in cases where # is specified for actual parameters that can be passed to generically typed formal parameters. These rules prevent syntax errors in programs that call a procedure and the procedure makes the full typing of a formal parameter type more general by switching to a generic type.

4.5 Conditional Operator, Type Inference

This example demonstrates a type inference for the conversion operator COND.

Source Code

REPORT demo_cond_type_inference.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    TYPES c10 TYPE c LENGTH 10.
    CLASS-METHODS:
      main,
      meth1 IMPORTING p TYPE c10,
      meth2 IMPORTING p TYPE c,
      meth3 IMPORTING p TYPE csequence,
      descr IMPORTING p TYPE any.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    FIELD-SYMBOLS <fs> TYPE any.
    DATA txt TYPE c LENGTH 20.
    DATA num TYPE i.
    ASSIGN num TO <fs>.

    demo=>meth1( p = COND #( WHEN 1 = 1 THEN txt ) ).
    demo=>meth1( p = COND #( WHEN 1 = 1 THEN txt ) ).
    demo=>meth1( p = COND #( WHEN 1 = 1 THEN <fs> ) ).
    cl_demo_output=>line( ).

    demo=>meth2( p = COND #( WHEN 1 = 1 THEN txt ) ).
   "demo=>meth2( p = COND #( WHEN 1 = 1 THEN num ) ).  "not possible
   "demo=>meth2( p = COND #( WHEN 1 = 1 THEN <fs> ) ). "not possible
    cl_demo_output=>line( ).

    demo=>meth3( p = COND #( WHEN 1 = 1 THEN txt ) ).
    demo=>meth3( p = COND #( WHEN 1 = 1 THEN num ) )  ##type.
    demo=>meth3( p = COND #( WHEN 1 = 1 THEN <fs> ) ) ##type.
    cl_demo_output=>display( ).
  ENDMETHOD.
  METHOD meth1.
    descr( p ).
  ENDMETHOD.
  METHOD meth2.
    descr( p ).
  ENDMETHOD.
  METHOD meth3.
    descr( p ).
  ENDMETHOD.
  METHOD descr.
    DATA type   TYPE string.
    DATA length TYPE i.
    DESCRIBE FIELD p TYPE type.
    IF type = 'g'.
      type = 'STRING'.
      length = strlen( p ).
    ELSE.
      DESCRIBE FIELD p LENGTH length IN CHARACTER MODE.
    ENDIF.
    cl_demo_output=>write( |{ type } { length }| ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

Passes constructor expressions with the conditional operator COND to differently typed formal parameters of methods. In the case of generic formal parameters, special rules apply when identifying the operand type. Replacing the conditional operator COND with SWITCH produces the same results.

◈ Fully typed formal parameter

When the method meth1 is called with a fully typed formal parameter, the operand type for # is identified using this parameter and the result of the conditional expression is converted to c with length 10 in all three calls.

◈ Formal parameter typed generically with c
The operand type for # is identified from the data type of the operand after THEN.

     ◈ In the first call, the type c with length 20 of the operand after THEN matches the generic type and is used.
     ◈ In the second call, the type i of the operand after THEN does not match the generic type and the call is not possible.
     ◈ In the third call, no type can be derived from the generically typed field symbol <fs> after THEN. No call is possible here, since there is no inference rule for the generic type c.

◈ Formal parameter typed generically with csequence

     ◈ In the first call, the type c with length 20 of the operand after THEN matches the generic type and is used.
     ◈ In the second call, the type i of the operand after THEN does not match the generic type and the type string is used. This is indicated by a syntax check warning.
     ◈ In the third call, no type is derived from the generically typed field symbol <fs> after THEN and the type string is used. This is indicated by a syntax check warning.

5. Iteration Expressions

An iteration expression is a subexpression of specific constructor expressions introduced using the iteration operator FOR and performs an iteration here. Alongside the optional use for creating rows in internal tables with the corresponding variants of the instance operator NEW and the value operator VALUE, a special reduction operator REDUCE is available that is based entirely on iteration expressions.

5.1 FOR - Iteration Expressions.

Syntax

... REDUCE| NEW| VALUE type( ... FOR ... UNTIL|WHILE ...| ... IN ... ... ) ...

Effect

The language element FOR introduces an iteration expression as a subexpression in constructor expressions with the reduction operator REDUCE and in the variants of the instance operator NEW and the value operator VALUE for creating internal tables.

There are two main variants of iteration expressions:

◈ Iteration expressions with UNTIL or WHILE for conditional iterations. These expressions are used to create (iteratively) the results of any data types using REDUCE or to create rows of internal tables using NEW or VALUE. The iteration steps can be defined as required.
◈ Iteration expressions with IN for table iterations. These expressions are used for table reductions using REDUCE or table comprehensions using NEW or VALUE. The iteration steps here evaluate an existing internal table.

Notes

◈ Any FOR variants can be combined in a single constructor expression, where they produce nested iterations.
◈ The reduction operator REDUCE must contain at least one iteration expression. The variants of the instance operator NEW and the value operator VALUE for creating internal tables can contain iteration expressions.
◈ The conditional iterations using UNTIL or WHILE provide an expression-oriented alternative to the loop statements DO and WHILE. The same applies to the table iterations using IN and the statement LOOP. The direct use of operand positions by the expression-oriented variants helps to avoid using auxiliary variables. Furthermore, they also enable certain tasks, such as creating values iteratively, to be expressed more concisely and more elegantly.

5.1.1 FOR - Conditional Iteration

Syntax

... FOR var = rhs [THEN expr] UNTIL|WHILE log_exp [let_exp] ...

Effect

This syntax form of an iteration expression executes a conditional iteration.

◈ When this syntax is used in a constructor expression with the reduction operator REDUCE, the reduction result is created in the iteration steps.
◈ If this syntax is used in a constructor expression with the instancing operator NEW or with the value operator VALUE for internal tables, new table rows are created in the iteration steps und added to the tabular result.

The parameters and arguments of the iteration expression must be specified as follows:

◈ First a local auxiliary variable var must be declared as an iteration variable and a start value rhs with = must be assigned to this auxiliary variable. The same applies to the namespace and visibility of var as applies to the auxiliary fields declared in a LET expression. The syntax of the declaration is exactly the same as in a LET expression and it follows the rules that apply here.

◈ The next position depends on the data type of the iteration variable var:

◈ If the iteration variable var does not have a numeric data type and is not of type d or t, an expression expr is specified after THEN. The result of this expression can be converted into a data type of var. The expression is calculated for every iteration and its result is assigned to the iteration variable var. This is a general expression position.
◈ If the iteration variable var has a numeric data type, or the variable is of type d or t, THEN expr is optional. If THEN expr is not explicitly specified, THEN var + 1 is implicitly added or the value of the iteration variable is increased by 1 for every iteration.

◈ Next a termination condition log_exp must be specified after UNTIL or WHILE. log_exp is any logical expression whose operands can be all visible data objects and any possible calls at this place.

◈ If the termination condition is specified after UNTIL, the logical expression log_exp is evaluated after every iteration step. If the result of the logical expression is true, the iteration is ended. At least one iteration step is executed.
◈ If the termination condition is specified after WHILE, the logical expression log_exp is evaluated after every iteration step. If the result of the logical expression is false, the iteration is ended. If the result of the logical expression is false even before the first iteration step, no iteration steps are executed.

◈ A LET expression let_exp can be specified (optional) at the end to define local auxiliary fields. The auxiliary fields are used in every iteration step and can be used to construct the result.

The variables declared in FOR expressions are local. The local data from all outer FOR expressions can be used when their values are defined. The iteration variable and auxiliary variables can be used after the FOR expression; either in additional subexpressions or to construct the result.

The system field sy-index is not set by a FOR expression.

Notes

◈ Usually the expression expr (after THEN) and the termination condition log_exp (after UNTIL or WHILE) depend on the iteration variable var. However this not mandatory. The value of the iteration variable or the termination condition can also be determined in other ways. For example, status changes can be retrieved using method calls.
◈ Usually a termination condition after UNTIL is preferable to a termination condition after WHILE in all cases where the termination condition does not have to be checked before the first iteration step.
◈ In many cases, iteration expressions for conditional iterations can replace DO and WHILE loops, which construct values and internal tables.
◈ Multiple sequential FOR expressions with different variants (including the tabular iterations) can be specified in a constructor expression. These expressions then work in the same way as nested loops.
◈ The profile parameter rdisp/max_wprun_time limits the maximum execution time of an ABAP program. If this runtime is exceeded because the termination condition does not occur in time, the program is ended by the runtime environment.
◈ Unlike in a LET expression, a local field symbol cannot be declared in place of the iteration variable var.

5.1.2 Examples of Iteration Expressions

- - Creating Values with FOR and REDUCE 

he example demonstrates conditional iterations with the operator REDUCE.

Source Code

REPORT demo_reduce_cond_iteration.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA(out) = cl_demo_output=>new(
      )->next_section( 'Summation'
      )->write( REDUCE i( INIT sum = 0
                          FOR n = 1 UNTIL n > 10
                          NEXT sum = sum + n )
      )->next_section( 'Concatenation without THEN'
      )->write( REDUCE string( INIT text = `Count up:`
                               FOR n = 1 UNTIL n > 10
                               NEXT text = text && | { n }| )
      )->next_section( 'Concatenation with THEN'
      )->write( REDUCE string( INIT text = `Count down:`
                               FOR n = 10 THEN n - 1 WHILE n > 0
                               NEXT text = text && | { n }| )
      )->next_section( 'Non arithmetic expression'
      )->write( REDUCE string( INIT text = ``
                               FOR t = `x` THEN t && `y`
                                           UNTIL strlen( t ) > 10
                               NEXT text = text && |{ t } | )
      )->display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

This example demonstrates the following simple iterations, which can be used to construct elementary data objects in a constructor expression using the reduction operator REDUCE and display them directly:

◈ The first expression constructs a value of the type i for which the total of the iteration variable n is calculated. n is numeric, which means that THEN does not need to be specified. Each iteration step raises the value of n by 1.
◈ The second expression uses the same iteration as the first, but constructs a text string to which each iteration step appends the character representation of n.
◈ Like the second expression, the third expression constructs a text string, but uses the addition THEN explicitly to decrement the iteration variable instead of incrementing it.
◈ Finally, the fourth expression demonstrates that the iteration variable does not have to be numeric. In this case, THEN must be specified explicitly with a suitable expression. Here, a text string is made longer until it has reached a specific size.

- - Creating Tables Using FOR and VALUE 

The example demonstrates conditional iterations with the operator VALUE.

Source Code

REPORT demo_value_cond_iteration.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    TYPES:
      BEGIN OF line,
        col1 TYPE i,
        col2 TYPE i,
        col3 TYPE i,
      END OF line,
      itab TYPE STANDARD TABLE OF line WITH EMPTY KEY.

    cl_demo_output=>write(
        VALUE itab(
          FOR j = 11 THEN j + 10 WHILE j < 40
          ( col1 = j col2 = j + 1 col3 = j + 2  ) ) ).

    cl_demo_output=>write(
        VALUE itab(
          FOR j = 31 THEN j - 10 UNTIL j < 10
          ( col1 = j col2 = j + 1 col3 = j + 2  ) ) ).

    cl_demo_output=>display( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

This example demonstrates the construction of internal tables using condition iterations with a constructor expression and the corresponding variant of the value operator VALUE. Two internal tables with different iterations are created and the output produced directly.

- - Creating a Matrix Using FOR and VALUE 

This example demonstrates how a matrix is created using iterations.

Source Code

REPORT demo_matrix.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    TYPES: t_column TYPE STANDARD TABLE OF string   WITH EMPTY KEY,
           t_rows   TYPE STANDARD TABLE OF t_column WITH EMPTY KEY.
    CLASS-METHODS: main.
  PRIVATE SECTION.
    CLASS-DATA:
      rows    TYPE i,
      columns TYPE i,
      x       TYPE i VALUE 1,
      y       TYPE i VALUE 1.
    CLASS-METHODS initialize.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    initialize( ).

    "Old way
    DATA: column     TYPE t_column,
          matrix_old TYPE t_rows.
    DO columns TIMES.
      DATA(idx) = sy-index - 1.
      CLEAR column.
      DO rows TIMES.
        APPEND sy-abcde+idx(1) && |{ sy-index }| TO column.
      ENDDO.
      APPEND column TO matrix_old.
    ENDDO.

    "New way
    DATA(matrix_new) =
      VALUE t_rows(
        FOR i = 0 UNTIL i > columns - 1 (
          VALUE t_column(
            FOR j = 1 UNTIL j > rows
              ( sy-abcde+i(1) && |{ j }| ) ) ) ).

    ASSERT matrix_new = matrix_old.

    TRY.
        cl_demo_output=>display( matrix_new[ x ][ y ] ).
      CATCH cx_sy_itab_line_not_found.
        cl_demo_output=>display( 'Not found' ).
    ENDTRY.
  ENDMETHOD.
  METHOD initialize.
    rows = 100.
    columns = strlen( sy-abcde ).
    cl_demo_input=>add_field( CHANGING field = x ).
    cl_demo_input=>add_field( CHANGING field = y ).
    cl_demo_input=>request( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

The rows and columns of a matrix are simulated using an internal table whose row types are arrays. These tables are filled using nested DO loops and equivalent iterations expressions for conditional iterations in a constructor expression with the operator VALUE. The ASSERT statement shows that both internal tables have the same content. It is possible to access each individual element.

- - Inverse Reads on Internal Table with FOR

This example demonstrates how sequential inverse reads are performed on an internal table.

Source Code

REPORT demo_value_cond_iteration.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS main.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA
      itab TYPE STANDARD TABLE OF i
           WITH EMPTY KEY
           WITH NON-UNIQUE SORTED KEY sort_key COMPONENTS table_line.

    itab = VALUE #( ( 2 ) ( 5 ) ( 1 ) ( 3 ) ( 4 ) ).

    DATA(output) =
      REDUCE string(
        INIT o = ``
        FOR  i = lines( itab ) THEN i - 1 WHILE i > 0
        NEXT o = o && COND #( LET r = itab[ KEY sort_key INDEX i ] IN
                              WHEN r > 2 THEN r && ` ` ) ).

    cl_demo_output=>display( output ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

In a conditional iteration, an internal table is read using WHILE and a table expression. The table expression uses the secondary table key sort_key and the result is created in a conditional expression using cond. The example shows how to bypass the restriction that inverse table iterations cannot be performed.

5.2 REDUCE - Reduction Operator

Syntax

... REDUCE type(
      [let_exp]
      INIT {x1 = rhs1}|{<x1> = wrexpr1}|{x1|<x1> TYPE dtype1}
           {x2 = rhs2}|{<x2> = wrexpr2}|{x2|<x2> TYPE dtype2}
           ...
      FOR for_exp1
      FOR for_exp2
      ...
      NEXT ...
           {x1 = rhs1}|{<x1> = wrexpr1}
           {x2 = rhs2}|{<x2> = wrexpr2}
           ... ) ...

Effect

A constructor expression with the reduction operator REDUCE creates a result of a data type specified using type from one or more iteration expressions. The following can be specified for type:

◈ A non-generic data type dtype.
◈ The # character as a symbol for the operand type.
     ◈ If the data type required in an operand position is unique and fully identifiable, this type is used.
     ◈ If the operand type cannot be identified in full, the type of the first declaration (which can always be identified) is used after INIT, except if the constructor expression is passed to an actual parameter with generically typed formal parameter.
     ◈ If the constructor expression is passed to an actual parameter with generically typed formal parameter, the operand type is derived in accordance with special rules.

The parameters and arguments of the constructor expression must be specified as follows:

◈ Firstly, an optional LET expression can be specified for defining local auxiliary fields, whose value is used within the expression but can no longer be modified there.
◈ An addition INIT must then be specified followed by any number of declarations. The declarations after INIT create local variables x1, x2, ... or field symbols <x1>, <x2>, ..., for which the same applies with respect to namespace and visibility as to the auxiliary fields declared in a LET expression. The syntax of the declaration is either
     ◈ exactly the same as in a LET expression and it follows the rules that apply here.
     ◈ or initial variables or field symbols of the specified data type dtype can be created using TYPE.
At least one variable or one field symbol must be specified. The variables or field symbols declared after INIT can only be used after NEXT. In particular, they cannot be used to declare other variables or field symbols.
     ◈ The first variable x1 or the first field symbol <x1> determines the result of the expression and the data type must be convertible to the result type type.
     ◈ All other variables or field symbols declared after INIT are optional auxiliary fields that can be on the left side of assignments after NEXT (unlike those declared after LET).
◈ At least one iteration expression must then be specified using FOR and it is also possible to specify multiple consecutive iteration expressions.
     ◈ If conditional iterations are used, the result is created in freely defined iteration steps.
     ◈ If table iterations are used, the rows of existing internal tables are evaluated. This is known as a table reduction, since any values can be constructed from the rows of existing internal tables.
◈ Assignments must then be specified after NEXT that are executed for every iteration of the last FOR expression. A value or memory area must be assigned to all variables and field symbols declared after INIT. In the case of structured variables or field symbols, it is enough to make an assignment to one of their components.
     ◈ The assignment on the right side rhs to variables x is made in accordance with the rules of a regular assignment using the assignment operator =, which means that any of the potential calls and expressions can be specified for rhs.
     ◈ Only the result of a writable expression wrexp can be assigned to a field symbol. The same applies here as in assignments of writable expressions using ASSIGN, which means that only the expressions specified there can be used.

The assignments can be specified in any order and they are evaluated from to left to right. Each non-structured variable or field symbol declared after INTIT can be specified precisely once as the left side. In the case of structured variables or field symbols, either the full structure can be specified as the left side or each component once. Alongside the data objects of the program, all local variables and field symbols of the expression visible after the last FOR expression can be used in the operand positions on the right side:

◈ All variables and field symbols declared after LET
◈ All variables and field symbols declared after INIT
◈ The wa1, wa2, ... or <fs1>, <fs2>, .... filled by the FOR expressions. This enables the content of the rows of the internal tables to be accessed and the result constructed.

Any auxiliary fields declared after INTIT can only be specified in reading positions on the right side.

After the full evaluation, the content of the first variable x1 or the memory area pointed to by the first field symbol <x1> is assigned to the temporary result of the expression of type type in accordance with the assignment rules.

Notes

◈ The name of the operator REDUCE is explained as follows:
     ◈ Conditional iterations are generally used to reduce sets of data objects to a single data object.
     ◈ Table iterations for table reductions are generally used to reduce the content of the internal tables in question to a summary value. The result type can be any type and does not have to be less complex than the internal tables, but this property should only be exploited in exceptional cases. When constructing tables from tables, it is best to use table comprehensions instead.
Table filtering can be implemented more efficiently using the filter operator FILTER.
◈ When a local field symbol <xi> is used on the left side of assignments after NEXT, it should be noted that the assignments are not made to the value referenced by the field symbol (as in INIT and LET). Instead, the field symbol is set in the same way as in the statement ASSIGN.
◈ Unlike in table comprehensions with the value operator VALUE, an assignment of a REDUCE expression to a structure or an internal table does not overwrite them in full before the FOR expressions are processed. The structure or internal table can be used directly anywhere in the expression.
◈ If a variable is declared in the first position after INIT and the VALUE operator is specified with the character # on the right side, the result type type of the REDUCE expression is used for this character if possible.
◈ Table reductions are also possible with FOR expressions for mesh paths.

5.2.1 REDUCE - Type Inference for Actual Parameter

If a constructor expression

REDUCE #( ... INIT ... FOR ... )

is passed to generically typed formal parameters as an actual parameter using the character # as a symbol for the operand type, the following type inference is performed for the character #:

◈ If the data type of the first declaration after INIT matches the generic type of the formal parameter, this data type is used.
◈ If the data type of the first declaration after INIT does not match the generic type of the formal parameter, the type is derived from the generic type as follows:

◈ string for csequence and clike
◈ xstring for xsequence
◈ decfloat34 for numeric and decfloat
◈ p with the length 8 and no decimal places if p is generic
◈ The standard key for a standard table type with generic primary table key

Other generic data types (except for table types) that are explicitly generic with respect to their secondary table keys cannot be made more concrete and produce a syntax error. More specifically, this applies to the types c, n, and x with generic lengths.

Notes

◈ The data type of the first declaration after INIT is always identified statically.
◈ Rules apply when deriving the type in cases where # is specified for actual parameters that can be passed to generically typed formal parameters. These rules prevent syntax errors in programs that call a procedure and the procedure makes the full typing of a formal parameter type more general by switching to a generic type.

5.2.2 Reduce Operator, Type Inference

This example demonstrates a type inference for the conversion operator RFC.

Source Code

REPORT demo_reduce_type_inference.

CLASS demo DEFINITION.
  PUBLIC SECTION.
    TYPES c10 TYPE c LENGTH 10.
    CLASS-METHODS:
      main,
      meth1 IMPORTING p TYPE c10,
      meth2 IMPORTING p TYPE c,
      meth3 IMPORTING p TYPE csequence,
      descr IMPORTING p TYPE any.
ENDCLASS.

CLASS demo IMPLEMENTATION.
  METHOD main.
    DATA txt TYPE c LENGTH 20.
    DATA num TYPE i.

    demo=>meth1( p = REDUCE #( INIT r1 = txt
                               FOR i = 1
                               UNTIL i > 9
                               NEXT r1 = r1 && 'x' ) ).
    demo=>meth1( p = REDUCE #( INIT r2 = num
                               FOR i = 1
                               UNTIL i > 9
                               NEXT r2 = r2 + 1 ) ).
    cl_demo_output=>line( ).

    demo=>meth2( p = REDUCE #( INIT r1 = txt
                               FOR i = 1
                               UNTIL i > 9
                               NEXT r1 = r1 && 'x' ) ).
    "demo=>meth2( p = REDUCE #( INIT r2 = num
    "                           FOR i = 1
    "                           UNTIL i > 9
    "                           NEXT r2 = r2 + 1 ) ). "not possible
    cl_demo_output=>line( ).

    demo=>meth3( p = REDUCE #( INIT r1 = txt
                               FOR i = 1
                               UNTIL i > 9
                               NEXT r1 = r1 && 'x' ) ).
    demo=>meth3( p = REDUCE #( INIT r2 = num
                               FOR i = 1
                               UNTIL i > 9
                               NEXT r2 = r2 + 1 ) ) ##type.
    cl_demo_output=>display( ).
  ENDMETHOD.
  METHOD meth1.
    descr( p ).
  ENDMETHOD.
  METHOD meth2.
    descr( p ).
  ENDMETHOD.
  METHOD meth3.
    descr( p ).
  ENDMETHOD.
  METHOD descr.
    DATA type   TYPE string.
    DATA length TYPE i.
    DESCRIBE FIELD p TYPE type.
    IF type = 'g'.
      type = 'STRING'.
      length = strlen( p ).
    ELSE.
      DESCRIBE FIELD p LENGTH length IN CHARACTER MODE.
    ENDIF.
    cl_demo_output=>write( |{ type } { length }| ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  demo=>main( ).

Description

Passes constructor expressions with the conversion operator REDUCE to differently typed formal parameters of methods. In the case of generic formal parameters, special rules apply when identifying the operand type.

◈ Fully typed formal parameter

When the method meth1 is called with a fully typed formal parameter, the operand type for # is identified using this parameter and the result of the reduction is converted to c with length 10 in both calls.

◈ Formal parameter typed generically with c

The operand type for # is identified from the first declaration after INIT.

     ◈ In the first call, the type c with length 20 of the declaration after INIT matches the generic type and is used.
     ◈ It is not possible to perform a call with the type i of the declaration after INIT, since it does not match the typing and there are no inference rules for the generic type c.

◈ Formal parameter typed generically with csequence

     ◈ In the first call, the type c with length 20 of the declaration after INIT matches the generic type and is used.
     ◈ In the second call, the type i of the declaration after INIT does not match the generic type and the type string is used. This is indicated by a syntax check warning.

«« Previous
Next »»

1 comment:

  1. I failed in my first attempt for Cisco exam because I couldn’t find a valid study guide. But then I downloaded Pass4sure Cisco dumps on the suggestion of my friend and got guaranteed success with exceptional grades. After my experience, I suggest other students to use Cisco questions and answers to make sure their success.

    ReplyDelete