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 DATA, DATA 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
Post a Comment
If you have any doubts let me know