ABAP News for Release 7.40 – Constructor Operator VALUE
With Release 7.40 ABAP supports so called constructor operators. Constructor operators are used in constructor expressions to create a result that can be used at operand positions. The syntax for constructor expressions is
… operator type( … ) …
operator is a constructor operator. type is either the explicit name of a data type or the character #. With # the data type can be dreived from the operand position if the operand type is statically known. Inside the parentheses specific parameters can be specified.
Value Operator VALUE
The value operator VALUE is a constructor operator that constructs a value for the type specified with type.
- … VALUE dtype|#( ) …
constructs an initial value for any data type.
- … VALUE dtype|#( comp1 = a1 comp2 = a2 … ) …
constructs a structure where for each component a value can be assigned.
- … VALUE dtype|#( ( … ) ( … ) … ) …
constructs an internal table, where for each line a value can be assigned. Inside inner parentheses you can use the syntax for structures but not the syntax for table lines directly. But you can nest VALUE operators.
Note that you cannot construct elementary values (which is possible with instantiation operator NEW) – simply because there is no need for it.
For internal tables with a structured line type there is a short form that allows you to fill columns with the same value in subsequent lines
VALUE dtype|#( col1 = dobj11 … ( col2 = dobj12 col3 = dobj13 … )
( col2 = dobj22 col3 = dobj23 … )
…
col1 = dobj31 col2 = dobj32 … ( col3 = dobj33 … )
( col3 = dobj43 … )
… ).
Example for initial values
Why would you like to construct an initial value anyhow? Well, you can pass an initial actual parameter to a structured or tabular formal parameter without the need of an initial hleper variable now.
CLASS c1 DEFINITION.
PUBLIC SECTION.
TYPES: BEGIN OF t_struct,
col1 TYPE i,
col2 TYPE i,
END OF t_struct.
CLASS-METHODS m1 IMPORTING p TYPE t_struct.
ENDCLASS.
CLASS c1 IMPLEMENTATION.
METHOD m1.
…
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
c1=>m1( VALUE #( ) ).
Example for structures
Three different ways to construct the same nested structure:
TYPES: BEGIN OF t_col2,
col1 TYPE i,
col2 TYPE i,
END OF t_col2.
TYPES: BEGIN OF t_struct,
col1 TYPE i,
col2 TYPE t_col2,
END OF t_struct.
DATA: struct TYPE t_struct,
col2 TYPE t_col2.
“1
struct = VALUE t_struct( col1 = 1
col2-col1 = 1
col2-col2 = 2 ).
“2
col2 = VALUE t_col2( col1 = 1
col2 = 2 ).
struct = VALUE t_struct( col1 = 1
col2 = col2 ).
“3
struct = VALUE t_struct( col1 = 1
col2 = VALUE #( col1 = 1
col2 = 2 ) ).
Examples for internal tables
Elementary line type:
TYPES t_itab TYPE TABLE OF i WITH EMPTY KEY.
DATA itab TYPE t_itab.
itab = VALUE #( ( ) ( 1 ) ( 2 ) ).
Structured line type (RANGES table):
DATA itab TYPE RANGE OF i.
itab = VALUE #( sign = ‘I’ option = ‘BT’ ( low = 1 high = 10 )
( low = 21 high = 30 )
( low = 41 high = 50 )
option = ‘GE’ ( low = 61 ) ).
Other expressions in VALUE operator
Of course, the arguments of VALUE can be expressions or function calls:
TYPES t_date_tab TYPE TABLE OF string WITH EMPTY KEY.
DATA(date_tab) = VALUE t_date_tab(
( |{ CONV d( sy-datlo – 1 ) DATE = ENVIRONMENT }| )
( |{ sy-datlo DATE = ENVIRONMENT }| )
( |{ CONV d( sy-datlo + 1 ) DATE = ENVIRONMENT }| ) ).
So you can do a lot of crazy things now, but be aware of obfuscation …
Really handy little feature. Thanks for sharing that. I'll be looking to use that to reduce some of the coding for my local class data. Also liking the fact we can use expressions for the assignment.
Yes, best ABAP ever!!
Now we have to get rid of the blanks in between the brackets and we'll become the best friends 😉 (cc Hendrik Neumann )
I don't believe this will ever happen - it would break every single character operation using offsets and brackets...
Do you have an example where this could be a problem?
Can do:
var = gr_foo->bar(baz).
will try to find a public attribute named bar and transfer the first baz characters into var.
var = gr_foo->bar( baz )
will call a method named bar and transfer baz as a parameter.
As it's not possible to define a method with the same name like an attribute, the compiler should know what to do.
CLASS class DEFINITION.
PUBLIC SECTION.
METHODS main.
METHODS meth IMPORTING p1 TYPE data
RETURNING VALUE(r) TYPE string.
ENDCLASS.
CLASS class IMPLEMENTATION.
METHOD main.
DATA meth TYPE string.
DATA res TYPE string.
res = meth(2). "Offset/Length
res = meth( 2 ). "Method Call
ENDMETHOD.
METHOD meth.
ENDMETHOD.
ENDCLASS.
Damned, I've forgot the local vars. Thank you.
And the global vars 😉
DATA meth TYPE string.
CLASS class DEFINITION.
PUBLIC SECTION.
METHODS main.
METHODS meth IMPORTING p1 TYPE data
RETURNING VALUE(r) TYPE string.
ENDCLASS.
CLASS class IMPLEMENTATION.
METHOD main.
DATA res TYPE string.
res = meth(2). "Offset/Length
res = meth( 2 ). "Method Call
ENDMETHOD.
METHOD meth.
ENDMETHOD.
ENDCLASS.
You know, nobody is using global variables anymore... 😈
Yeah, in Utopia 😛
Assuming that the ABAP compiler works like other compilers do: It can't, because checking whether a method or variable exists is part of the linking process, and the buildup of the AST and the symbol tables happens way before that. If someone is interested in this, I might hack together a small blog article about this...
Interesting question. But maybe it can be solved because the ABAP compiler is a Multi Pass Compiler since 7.02.
In fact, we have the problem for inline declarations. It is solved by the following rule:
If a field named "data" is already declared in a context, the syntax "data(...)" at an operand position is not an inline declaration but an offset length access to "data". Only if "data" is not yet declared, the syntax "data(data)" declares "data".
😥
Let's just say: get rid of the mandatory blanks in between the brackets.. and while we're at it I would love to see the mandatory blanks surrounding assigments gone as well 😉 Something like this lv_number= 5. is just more expressive than lv_number = 5. Did see that first used by Kent Beck and liked it right away...
Unfortunately, in non-Unicode programs variable names can contain symbols 🙁 .
Still have to support non-Unicode programs.
Hello Horst,
Finally we have upgraded the ABAP Kernel to 740 & i have already started to get my hands dirty.
I am using your examples as reference to my learning. Is there any guideline to using the concrete datatype(dtype) or # while using VALUE operator?
For e.g., i can re-write your code as
struct = VALUE #( col1 = 1
col2 = VALUE #( col1 = 1
col2 = 2 )
).
What are your suggestions?
BR,
Suhas
Hi Suhas,
The syntax of all constructor expressions is:
... operator type( ... ) ...
where operator is NEW, VALUE, CONV, etc. and type specifies the data type of the return value.
For type you can either specify the name dtype of a concrete data type or #. With # you let the compiler deduce the appropriate data type for the operand position (operand type) where the constructor expression is used. This is appropriate in most cases and relieves you from finding and typing the name of the type yourself. Typical example: a method meth expects a structured input parameter of type mystruct. When calling the method you can write
meth( value #( col1 = ... col2 = ... ) )
The compiler implicitly replaces # by mystruct. You could also write
meth( value mystruct( col1 = ... col2 = ... ) )
but that is not necessary here. So, you always use #, if you want to use the operand type and it can be deduced by the compiler.
You use the explicit type, if you want to override the implicit operand type (normally not a use case), if you want to make your coding more clear by expressing the type explicitly, or if the compiler cannot deduce the operand type and you simply cannot use #. Example:
DATA(struct) = mystruct( col1 = ... col2 = ... ).
Here you must tell the compiler what type of structure you want to construct. If struct is already declared, you can use #:
struct = #( col1 = ... col2 = ... ).
Regards
Horst
Thanks for your reply
This is what i wanted to know, I think using # may lead to obfuscation especially when passing it to procedures 😐
It depends also on the operator.
For CONV, # is exactly what you need in most cases.
Before:
DATA text TYPE c LENGTH 255.
...
DATA helper TYPE string.
helper = text.
DATA(xstr) = cl_abap_codepage=>convert_to( source = helper ).
After:
DATA text TYPE c LENGTH 255.
...
DATA(xstr) = cl_abap_codepage=>convert_to( source = CONV #( text ) ).
I don't need to know what the type is and if the interface of the method is changed, your code has a good chance to survive.
Great tip Horst!
Hi Horst,
first of thank you for an absolutely amazing ABAP version. A lot of the feature come naturally and I use them on a daily basis.
One thing I just tried that isn't working as expected is the following:
DATA(lv_date) = VALUE dats( '20140929' ).
I get a syntax error that dats is not a structure.
I know I can rewrite this as:
DATA lv_date type dats value '20140929'.
but is there a way to get the first version working using the new features?
Regards
Adi
As per the SAP documentation of VALUE, it does -
Refer - ABAP Keyword Documentation
BR,
Suhas
Hi Suhas,
dats is not a generic type IMHO.
But Horst did give a nice solution in his reply.
adi
Hi Adi,
The resaon for the syntax error is, that VALUE does not allow you "construct" elementary types. One might argue "why not?" and in fact I did so often, but up to now this restriction is kept.
But the gap can simply be closed as follows:
DATA(lv_date) = CONV dats( '20140929' ).
Best
Horst
That's a smart workaround Horst 😎
Anyway was there any reason why it was decided why VALUE cannot be used to build the value of elementary types?
You should construct elementary values only where needed:
... NEW elem_type( elem_value ) ...
... VALUE table_type( ... ( elem_value ) ... ) ...
In all other elementary operand positions you can place the elementary operands directly, no need to construct them. The only use case - conversions - is handled by the dedicated conversion operator CONV. And be aware that you also get a syntax warning, if you use CONV superfluously, as e.g. here:
DATA int TYPE i.
int = CONV #( 555 ).
Another solution would have been to overload VALUE in a way that makes CONV superfluous. But the decision was to offer two distinct operators.
Hi Horst,
thanks that will works like a treat.
Regards
Adi
Let us assume I create the following types
TYPES: BEGIN OF ts_path,
caller TYPE i,
called TYPE i,
END OF ts_path.
TYPES: BEGIN OF ts_trace,
id TYPE satr_de_id,
path TYPE ts_path,
mod TYPE seocpdname,
END OF ts_trace.
With this, the following code compiles
DATA(ls_call) = VALUE
ts_trace( id = 'm'
path = VALUE #( caller = 3
called = 5 ) ).
while this does not:
DATA(ls_call2) = VALUE ts_trace(
id = 'm'
path = VALUE #( caller = 3 "<--- Error: Arithmetic calculation not permitted
called = 5 )
mod = 'MAIN' ).
Now, is this a bug or a feature ?
(the work around is to rename the field mod to something.. else).
regards,
JNN
Hi Jacques,
Seems that you are Mr. Bugfinder, No.1.
Writing it as
TYPES: BEGIN OF struc1,
col1 TYPE i,
col2 TYPE i,
END OF struc1.
TYPES: BEGIN OF struc2,
col1 TYPE c LENGTH 1,
col2 TYPE struc1,
col3 TYPE c LENGTH 10,
END OF struc2.
TYPES: BEGIN OF struc3,
col1 TYPE c LENGTH 1,
col2 TYPE struc1,
mod TYPE c LENGTH 10,
END OF struc3.
DATA(ls_call2) = VALUE struc2(
col1 = 'x'
col2 = VALUE #( col1 = 3
col2 = 5 )
col3 = 'XXX' ).
DATA(ls_call3) = VALUE struc3(
col1 = 'x'
col2 = VALUE #( col1 = 3 "<--- Error: Arithmetic calculation not permitted
col2 = 5 )
mod = 'XXX' ).
clearly hints to a bug.
I forwarded to development.
Cheers!
Horst
B.T.W., replacing mod with div gives the same error.
Hi Horst,
Thanks for sharing the new syntax in strcutred manner
just out of curiosity I did a perfomance analysis of the append . The new syntax was taking thrice time of the old syntax.
TYPES ty_itab_value TYPE TABLE OF i WITH EMPTY KEY.
DATA itab_value TYPE ty_itab_value.
get RUN TIME FIELD DATA(lv_fld1).
itab_value = VALUE #( ( ) ( 1 ) ( 2 ) ).
get RUN TIME FIELD DATA(lv_fld2).
DATA(lv_fld_diff) = lv_fld2 - lv_fld1.
WRITE: / lv_fld_diff.
clear:itab_value.
get RUN TIME FIELD DATA(lv_fld3).
APPEND 0 to itab_value.
APPEND 1 to itab_value.
APPEND 2 to itab_value.
get RUN TIME FIELD DATA(lv_fld4).
DATA(lv_fld_diff1) = lv_fld4 - lv_fld3.
WRITE: / lv_fld_diff1.
The result was 3:1. Ofcourse this is not a serious test(but without scalable data) . Is there any doc where we have perf impications comapred to older syntax.
Regards
Arshad
Hi Arshad,
hehe, you are right 😉
I've tested it within a loop (DO 10 million TIMES...) and the ration changed to 4:3. The new syntax is still a bit slower, but we are talking about milliseconds, so not really a problem, isn't it?
With your "test" you can also show the opposite behavior 🙂
TYPES ty_itab_value TYPE TABLE OF i WITH EMPTY KEY.
DATA itab_value TYPE ty_itab_value.
"itab_value = VALUE #( ( ) ( 1 ) ( 2 ) ).
clear:itab_value.
get RUN TIME FIELD DATA(lv_fld3).
APPEND 0 to itab_value.
APPEND 1 to itab_value.
APPEND 2 to itab_value.
get RUN TIME FIELD DATA(lv_fld4).
DATA(lv_fld_diff1) = lv_fld4 - lv_fld3.
WRITE: / lv_fld_diff1.
clear:itab_value.
get RUN TIME FIELD DATA(lv_fld1).
itab_value = VALUE #( ( ) ( 1 ) ( 2 ) ).
get RUN TIME FIELD DATA(lv_fld2).
DATA(lv_fld_diff) = lv_fld2 - lv_fld1.
WRITE: / lv_fld_diff.
You measure the creation of the internal table in your first go that takes more time than filling it.
If you take away the " above, the internal table is created before both measurements ...
Hi Horst,
Before 7.4 the below code inserted a new line into table itab.
Start-of-selection.
my_class=>my_method( changing ct_tab = itab ).
Method my_method.
Insert c1=>m1( ) into table ct_tab. "ct_tab is changing param
Endmethod.
Now in 7.4 this code overwrites the content of ct_tab instead of inserting a new line.
Method my_method.
ct_tab = VALUE #( ( c1=>m1( ) ) ).
Endmethod.
Where:
Method m1.
rs_line = ls_line. "rs_line is returning param
Endmethod.
I want to insert a new line into the itab - where am I going wrong?
Regards,
Sougata.
Aren't you are using the wrong command (VALUE)?
I was working with the 7.4 example:
Before 7.4:
Data:
itab type tt,
wa like line of itab.
wa-c1 = 7.
wa-c2 = 9.
INSERT wa INTO TABLE itab.
wa-c1 = 3.
wa-c2 = 5.
INSERT wa INTO TABLE itab.
Instead in 7.4:
Data(itab) = VALUE tt( ( c1 = 7 c2 = 9 )
( c1 = 3 c2 = 5 ) ).
Is it not possible to use the same inside a method instead of INSERT or APPEND?
Well,
VALUE is a constructor and not insert command.
You may use (... VALUE #( ... ( LINES OF itab ) ... ) ...) for this purpose.
Check ABAP News for 7.40, SP08 - Start Value for Constructor Expressions for further details.
Thanks but I'm on 7.4 SP05 so this syntax will not work.
With 7.40, SP08, there will be the BASE addition to constructor operators to achieve this.
http://scn.sap.com/community/abap/blog/2014/09/29/abap-news-for-740-sp08--start-value-for-constructor-expressions
When you construct the structure with the operator #, this code cannot be found via where-used list.
Example:
lo_obj->set_structure( VALUE #( field1 = 'aaa' field2= 'bbb'' ).
The type of importing structure is STRUCT_TYPE
When you enter where used list on STRUCT_TYPE, this line of code does not appear.
It can be "solved" via replacing # with STRUCT_TYPE:
lo_obj->set_structure( VALUE struct_type( field1 = 'aaa' field2= 'bbb'' ).
but it's not the nicest workaround, I think.
Regards,
Radek
Hi Horst
I'v been using this new syntax for a while now, and until today everything was fine, but today I stumbled upon something that surprised me, the next line doesn't compile:
data(itab) = VALUE string_table( ( 'string1' ) ( 'string2' ) ).
Error is "'string1'" and the line of "ITAB" are incompatible., while this other line does compile:
data(itab) = VALUE string_table( ( |string1| ) ( |string2| ) ).
I guess it has something to do with Text field literals and Text string literals, but still find it weird as all other assigments between text field literals and strings work, even when calling methods like this:
CLASS lcl_test DEFINITION.
PUBLIC SECTION.
CLASS-METHODS test IMPORTING st TYPE string.
ENDCLASS.
CLASS lcl_test IMPLEMENTATION.
METHOD test.
WRITE st.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
lcl_test=>test( 'string' ).
So, any ideas on why this happens?
btw thanks for the blog, I find it very interesting.
Regards
Simón
.
Best
Hi Horst.
Is there any way to debug step by step the VALUE operator? Like LOOP AT.
i.e.
data(z) = value t_table( for wa in some_table where ( <some condition> )
( field1 = cond #( when wa-field1 > 0 then x else y ) ).
In SAP GUI, you can change the "Step Size" to debug expressions. In ADT not yet.
Never used that button 😀 Thanks!
TYPES: BEGIN OF ty_ship,
tknum TYPE tknum, "Shipment Number
name TYPE ernam, "Name of Person who Created the Object
city TYPE ort01, "Starting city
route TYPE route, "Shipment route
END OF ty_ship.
TYPES: ty_ships TYPE SORTED TABLE OF ty_ship WITH UNIQUE KEY tknum.
TYPES: ty_citys TYPE STANDARD TABLE OF ort01 WITH EMPTY KEY.
GT_SHIPS type ty_ships. -> has been populated as follows:
R0001
DATA(gt_citys) = VALUE ty_citys( FOR ls_ship IN gt_ships ( ls_ship-city ) ).
Above line will copy CITY column data into GT_CITYS.
What we should do if we need Column ROUTE data along with CITY?.
I want to copy all the lines of CITY and ROUTE data to another table.
Use the syntax for structures within the brackets, ( comp1 = ... comp2 = ... ).
Horst
Thanks Horst Keller.. It worked.
Hi thanks for your blogs
On performance - is there a performance gain when you use VALUE expression to move internal table contents from itab1 to itab2 instead of looping through itab1 and appending to itab2?
Regards
No, internally the same kernel functions are called.
Hi Horst,
I've a question regarding VALUE with FOR:
DATA(del) = VALUE tt_vbeln( FOR line IN deliveries (
table_line = line-document_numb ) ).
An error is ocuring in "table_line". It says "Type "C" is not a structure".
Can't I use this?
Thank you.
Regards,
Luís
Hi Luís,
You cannot use the pseudo comment table_line inside the construction of a structured table line. The only position where table_line can be used in FOR is the WHERE condition.
The syntax error stems from the fact, that you use the syntax for constructing a structure. But the line type of tt_vbeln is not structured.
Instead, your code should simply worke like this:
DATA(del) = VALUE tt_vbeln( FOR line IN deliveries
( line-document_numb ) ).
Horst
Thank you! It worked fine!
Hello Horst,
I would like to ask how I could use VALUE operator with ‘FOR wa IN itab’, instead of traditional ‘Loop at itab assigning <fs>’, where in the loop I could remove lines and modify fields based on the values from other fields.
I tried with something like below, but it didn’t copy all lines from itab2, but only this for which field3 has been modified.
When I add ( wa ) to parameters or I used ‘BASE CORRESPONDING #( itab2 ) before ‘FOR’ – like below:
or:
… then all the lines have been copied + few additional lines with changed values of field3. So eventually there was more lines in itab1 then in itab2.
What would be the best approach for this kind of requirement ?
Kind Regards
Rafal
Hey!
According to the example "Structured line type (RANGES table)":
I would like to additonally use FOR/ CORRESPONDING:
But I get the following error at FOR:
The component "FIELD" and its superstructure "" were specified at the same time.
I get the error " ")" expected, not "("." when trying to integrate FIELD in the FOR loop:
Without the "(" it says:
Unable to interpret "CORRESPONDING". Possible causes of error include incorrect spellings or comma errors.
Is it not possible or am I doing something wrong?
The only way it worked is the straight way, but I would like to use corresponding:
Thanks for any hint.
This syntax?
Yes, cool! Thanks Sandra!
I tried similar syntax, but without the 2nd VALUE.
Thanks a lot!
Hi,
constructor operators are cute...
but now we need more power in real coding. :))
* reduce #( init x type betrw_kk for wa_topeb in gt_print_amount
* where ( ‘DGT/#ETUKB/FAIZ/GUC’ cs print_doc_item )
* next x = x + wa_topeb-amount )
not possible
* reduce #( init x type betrw_kk for wa_topeb in gt_print_amount
* where ( print_doc_item = ‘DGT’ or
* print_doc_item = ‘#ETUKB’ or
* print_doc_item = ‘FAIZ’ or
* print_doc_item = ‘RI’ or
* print_doc_item = ‘RC’ or
* print_doc_item = ‘ILAVE’ or
* print_doc_item = ‘KCK’ or
* print_doc_item cp ‘GUC*’ )
* next x = x + wa_topeb-amount )
nice but it can be more…
reduce #( init x type betrw_kk for wa_topeb in gt_print_amount
where ( print_doc_item in
value bizc_selopt_tab( sign = ‘I’ option = ‘EQ’
( low = ‘DGT’ )
( low = ‘#ETUKB’ ) ) )
next x = x + wa_topeb–amount )
*awkward, in need of a generic functional method to build range from a string or value list.
Why don't you define this functional method?
PS: SAP has never provided rich ABAP APIs so it's useless asking this kind of thing according to me. Instead, create your own library of utilities.