SAP ABAP expressions for generic and dynamic programming in ABAP Platform 2021 Part 1

SAP ABAP expressions for generic and dynamic

programming in ABAP Platform 2021:

Part A – Dynamic Access to (maybe generic) references

We use the new SAP ABAP expressions like constructor operators / table selectors in our coding? But we often find that when using generic programming, i.e., the data types like REF TO DATADATA or ANY you fall back to programming style of the 70th? So the new ABAP platform 2021 (which shipped with kernel 7.85 last week) has new features to get you clean up our coding.

Main mantra of the new release is to 'Get rid of field-symbols'

A. The old ways-  how to handle generic data references classically?

We were using non-generic references in ABAP you always could write the following as shown below-

DATA foo TYPE REF TO i.

...

foo->* = 5.

Here & in the following the CREATE DATA statement or NEW operator has been omitted.

We are using generically typed references this was not possible -

DATA foo TYPE REF TO data.

...

foo->* = 5.   *** Syntax error : No de-referencing of generic references possible.

This is the only possibility to access the variable 'foo' would be to use field symbols.

DATA foo TYPE REF TO data.

...

ASSIGN foo->* TO FIELD-SYMBOL(<fs>).

<fs> = 5.

It will make the code uglier & more difficult to read. This also makes dereferencing the reference impossible inside ABAP expressions, as there is no expression variant of ASSIGN. The another disadvantage is the tricky error handling of ASSIGN. We can have subtle bugs when the error handling is forgotten.

B. Dereferencing generic references is now possible: (nearly) everywhere!

The restriction has been lifted now. We can now use the dereferencing operator in most places in ABAP where you can use generically typed ABAP variables. A simple example would be:

DATA foo TYPE REF TO data.

...

my_object->meth( foo->* ).

So FOO is the initial reference, then a runtime error will occur, as in the non-generic case. then no error handling is necessary in most cases.

Yes this also works in ABAP SQL like follows:

DATA ref TYPE REF TO data.

...

SELECT * FROM T100

  INTO TABLE @ref->*.

It is however, immediately leads to a new question. The var REF is a 'REF TO DATA' and not a reference to an internal table type.

This is not possible in ABAP yet.  So this is simply no “REF TO TABLE” - type.

C. Generic References and Internal Tables

So in the past there are many circumstances we could not use field symbols of type ANY or variables of type DATA to access internal tables.

FIELD-SYMBOLS <any> TYPE any.

...

READ TABLE <any> ASSIGNING FIELD-SYMBOL(<line>)

  WITH TABLE KEY (dyn_key) = value. ***Syntax error : <any> is no internal table

Tip: I am using a dynamic key specification here.

We had to manually 'reassign' the field symbol like follows -

FIELD-SYMBOLS <any> TYPE any.

FIELD-SYMBOLS <table> TYPE any table.

...

 

ASSIGN <any> TO <table>.

IF sy-subrc <> 0.

  ... " error handling!

ENDIF.

 

READ TABLE <table> ASSIGNING FIELD-SYMBOL(<line>)

  WITH TABLE KEY (dyn_key) = value.

it makes the coding at least five lines longer than usual, because of the error handling & the check for sy-subrc. This is also error prone, as we might forget the error handling, which yields all kinds of funny results if we do this inside a loop & the field symbol of the last loop iteration are still assigned.

We can now use variables & field-symbols of type ANY & DATA directly in LOOP and READ statements. It gives us many new possibilities:

DATA ref TO REF TO data.

...

LOOP AT ref->* ASSIGNING FIELD-SYMBOL(<fs>).   " now possible

ENDLOOP.

 

READ TABLE ref->* ASSIGNING FIELD-SYMBOL(<fs>)  " now possible

  WITH KEY (dyn_key) = value.

This makes it possible to directly dereference a reference & apply a table selector.

DATA itab_ref TYPE REF TO data.

...

itab_ref->*[ (dyn_key) = key_value ] = value.

So Same mechanism has been applied to internal table functions like LINES:

DATA itab_ref TYPE REF TO data.

...

IF lines( itab_ref->* ) > 0.

...

ENDIF.

If in this case that ITAB_REF does not point to a an internal table at runtime, there is the new runtime error ITAB_ILLEGAL_OPERAND.

It is however a serious limitation to this. We can still not access variables of type DATA or ANY by index. We will still need a field-symbol of type INDEX TABLE.

DATA itab_ref TYPE REF TO data.

...

itab_ref->*[ 1 ] = value.                        " syntax error

itab_ref->*[ (dyn_key) = key_value ] = value.    " ok

 

D. Introducing Dynamic Reference Expressions

Above previous paragraphs are only one part of the solution. if the target of your reference is a structure type, which you don't know exactly at compile time? So in order to access the individual components of the structure?

So in the past you would have done something like this:

DATA struct_ref TYPE REF TO data.

...

ASSIGN struct_ref->* TO FIELD-SYMBOL(<fs>).

IF sy-subrc <> 0.

  " error handling 1

ENDIF.

ASSIGN COMPONENT 'COMP' OF STRUCTURE <fs> TO FIELD-SYMBOL(<fs2>).

IF sy-subrc <> 0.

  " error handling 2

ENDIF.

<fs2> = value.

Some colleagues even know the completely dynamic ASSIGN, where you could do this all in one step:

DATA struct_ref TYPE REF TO data.

...

ASSIGN ('STRUCT_REF->COMP') TO FIELD-SYMBOL(<fs>).

IF sy-subrc <> 0.

  " error handling

ENDIF.

<fs> = value.

It is a serious drawbacks:

  • We cannot do this inside expressions.
  • Here everything is dynamic. If we change the name of the variable STRUCT_REF, you will only know at runtime of there is an error.
  • Now ASSIGN is dangerous, because of the sy-subrc error handling you might forget.
  • We need many lines of code.

It is a very unknown variant of ASSIGN you could use -

DATA struct_ref TYPE REF TO data.

...

ASSIGN STRUCT_REF->('COMP') TO FIELD-SYMBOL(<fs>).

IF sy-subrc <> 0.

  " error handling

ENDIF.

<fs> = value.

You now decided that this gives a good hint for a new kind of ABAP expression, which you can use in many places in ABAP platform 2021. We can now write:

DATA foo TYPE REF TO data.

DATA comp_name TYPE string VALUE `comp`.

...

my_object->meth( foo->(comp_name) ).

We can use these new kind of expression in most places where you can use expressions and generically typed variables.

Component again of a reference to another structure / reference to a simple type? You have two possibilities.

  • Components name can be arbitrary assign expression like-  COMP->* or
    COMP-COMP2 or COMP->COMP2
  • We can use chaining on these new kind of expressions.

DATA foo TYPE REF TO data.

...

" assign expression:

my_object->meth( foo->('comp1->comp2->*') ).

 

" assign expression with structures:  

my_object->meth( foo->('comp1-comp2->*') ).

 

" new kind of daisy-chaining:

my_object->meth( foo->('comp1')->('comp2')->* ).

Yes we always get nice exceptions if the components do not exist / are not assigned.

No sy-subrc is set, of course!

So the last ex: with daisy chaining doesn't work with structures yet. So you cannot write:

DATA foo TYPE REF TO data.

...

my_object->meth( foo->('COMP')-('COMP2') ).

It might be a possible improvement in later ABAP releases.

Yes it is a new feature which makes most sense, if we do not know the target type exactly. If we know the target type exactly at compile time, we can always do a  -

DATA foo TYPE REF TO data.

...

CAST concrete_type( foo )->comp = 5.

It makes of course even more sense, We you need to access many components of the structure in one method.

The thumb rule would be:

  • If we know the type of the reference exactly at compile time & need to access multiple components, you should opt for CAST #( ).
  • If we don’t know the type of the reference exactly, but only know that the target type contains a column named BLA, use REF->(‘BLA’)
  • If we just need a single component from REF but you know the target type of REF statically, both methods are possible. This depends on the context if you should prefer REF->(‘BLA’) or
    CAST concrete_type( ref )->bla. We would probably use the CAST #( ) more often in this case, as you will get syntax errors when the component is deleted or renamed in the original structure. it also enables 'where used' functionality. Yes we will get a stronger coupling to the specific type in this case, which might not be desired in all cases.

 

E. Dereferencing fully generic types

From the past we could only de reference variables which are explicitly typed as reference. Also but to allow daisy chaining this had to be lifted. This is why-

DATA foo TYPE REF TO data.

...

FOO->(COMP)->(COMP2) = 5.

         * ^______result of FOO->(COMP) is *no* reference,

         *             but of type DATA.

This is now also possible to dereference completely generic type. So an exception is thrown at runtime, if there were no reference is assigned to the variables.

METHODS foo IMPORTING value TYPE data.

...

METHOD foo.

  value->* = 5.   ***runtime error  value not reference.

ENDMETHOD.

A language theoretic point this is the following- Something cannot be checked at compile time but could be valid at runtime, we should not disallow it, but postpone the check to the runtime.

So in ASSIGN, this was always possible to dereference a fully generic variable. this is not really any safer option -

METHODS foo IMPORTING value TYPE data.

...

METHOD foo.

  FIELD-SYMBOLS <fs> TYPE REF TO i.

  ASSIGN value->* TO <fs>.

  IF sy-subrc <> 0.

    ... " error handling

  ENDIF.

ENDMETHOD.

 

F. Performance

So people ask for performance when new SAP ABAP expressions are introduced -

New SAP ABAP expressions described in this document have a very small performance penalty in comparison with their non generic counter parts. The following holds regarding performance- 

  • The new expressions are faster / equally fast than the ASSIGN command. If we do a proper error handling in ASSIGN, then the new expressions will be faster.
  • When we use the same expression many times in one ABAP method, using the old ASSIGN with a field symbol & continuously using that single field symbol is still a bit faster.

Then both assertions are not new, they apply [in a similar way] for nearly every other kind of SAP ABAP expression. So be aware regarding performance: measure, don’t guess. Always prefer the kind of coding which is most clean. if we have performance problems stick to less clean coding only.

Immediately leads to another question: Which is faster?

object->meth( BLA->('BLUB->BLOB->*') )

 

vs.

 

object->meth( BLA->('BLUB')->('BLOB')->* )

i.e.,. old style ASSIGN expressions v/s  the new kind of daisy-chaining.

So the answer of course depends. We generally assume that the old style ASSIGN expressions are a very very tiny bit faster if the chain is short. It [nearly unmeasurable] performance benefit should diminish for longer chains.

If we need SAP ABAP coding to produce  the string "BLUB->BLOB->" then the new daisy-chaining will have an advantage. It might depend on the context which of the two variants perform better. Also there we would stick to the coding which is more clear & more easily understandable.

G. Outlook

New expressions can provide an easier way to handle fully generic variables or references in SAP ABAP. It can be used in expressions, throw exceptions & do not set any sy-subrc. It can be combined to form even more powerful constructs.

As of SAP ABAP platform 2021 it still not every combination of the expressions is possible at every position in the SAP ABAP coding. It leaves room for [possible] improvements in later releases.

What are the additional stuff doesn't work at this moment?

G.1. Possible Improvement : Daisy-Chaining in ASSIGN for 'simple' variables

The Use of the chaining of generic reference components access in ASSIGN does not work at the moment, i.e.,. following does not work yet - 

DATA foo TYPE REF TO data.

...

" Syntax error in ABAP platform 2021:

ASSIGN foo->(comp_name)->* TO FIELD-SYMBOLS(<fs>).

This does only work outside of ASSIGN. Main reason is that the sy-subrc semantics as inside ASSIGN the sy-subrc must be set & no exception should be thrown.

Implementation choices in the original ASSIGN implementation and it does work when using a table selector though -

DATA itab TYPE TABLE OF REF TO  Data.

...

"No syntax error in ABAP platform 2021:

ASSIGN itab[ 1 ]->('BLA')->* TO FIELD-SYMBOL(<fs>).

So the latter sets sy-subrc to 4 then the itab is empty, but it will throw a RABAX if there is no component BLA or reference are not assigned. it's in sync with the behavior's in previous SAP ABAP releases.

G.2. Possible Improvement : Dynamic access to structure components

This moment the new dynamic expressions can only be used when the starting variable are references. If the starting variable is of type DATA / ANY or of structure type and points to a variable of structure type at runtime they provide no benefit.

Following would be imaginable as shown below:

METHODS foo IMPORTING value TYPE data.

...

METHOD foo.

  value-(comp_name) = 5.               " Syntax error in ABAP platform 2021

  value->(comp_name)-(struc_comp) = 5. " Syntax error in ABAP platform 2021

ENDMETHOD.

It could also be a possible future improvements.

H. Resume

This article you described how we:

  • Write better code when using generic data reference by using the arrow operator ->* directly inside expressions
  • the new dynamic reference expressions used to access components of generic data / object references.

 

Comments

Popular posts from this blog

EVENTS IN INTERACTIVE REPORTS OF SAP ABAP

SAP ABAP Fresher Resume/ CV Writing Format..

CONCEPTS OF INNER JOIN AND OUTER JOIN in SAP ABAP