ABAP News for Release 7.40 – Constructor Operators CONV and CAST
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.
Conversion Operator CONV
The conversion operator CONV is a constructor operator that converts a value into the type specified in type.
… CONV dtype|#( … ) …
You use CONV where you needed helper variables before in order to achieve a requested data type.
Example for parameter passing
Method cl_abap_codepage=>convert_to expects a string but you want to convert a text field.
Before release 7.40
DATA text TYPE c LENGTH 255.
DATA helper TYPE string.
DATA xstr TYPE xstring.
helper = text.
xstr = cl_abap_codepage=>convert_to( source = helper ).
With release 7.40
DATA text TYPE c LENGTH 255.
DATA(xstr) = cl_abap_codepage=>convert_to( source = CONV string( text ) ).
In such cases it is even simpler to write
DATA text TYPE c LENGTH 255.
DATA(xstr) = cl_abap_codepage=>convert_to( source = CONV #( text ) ).
Example for influencing a calculation
IF 1 / 3 > 0.
…
ENDIF.
is false, but
IF CONV decfloat34( 1 / 3 ) > 0.
…
ENDIF.
is true!
Example for influencing a comparison
The infamous
IF ‘ ‘ = ` `.
…
ENDIF.
is false. But
IF ‘ ‘ = CONV char1( ` ` ).
…
ENDIF.
is true!
Casting Operator CAST
The casting operator CAST is a constructor operator that executes an up or down cast for reference varaibles with the type specified in type.
… CAST dtype|class|interface|#( … ) …
- You use CAST for a down cast where you needed helper variables before in order to cast with ?= to a requested reference type.
- You use CAST for an up cast, e,g, with an inline declaration, in order to construct a more general type.
You can write a compnent selector -> directly behind CAST type( … ).
Example from RTTI
Common example where a down cast is needed.
Before release 7.40
DATA structdescr TYPE REF TO cl_abap_structdescr.
structdescr ?= cl_abap_typedescr=>describe_by_name( ‘T100’ ).
DATA components TYPE abap_compdescr_tab.
components = structdescr->components.
With release 7.40
DATA(components) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( ‘T100’ ) )->components.
Example with up cast
The static type of the reference variable iref declared inline should be the interface not the class.
INTERFACE if.
…
ENDINTERFACE.
CLASS cl DEFINITION CREATE PRIVATE.
PUBLIC SECTION.
INTERFACES if.
CLASS-METHODS factory RETURNING value(ref) TYPE REF TO cl.
…
ENDCLASS.
CLASS cl IMPLEMENTATION.
METHOD factory.
ref = NEW #( ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(iref) = CAST if( cl=>factory( ) ).
Example with data objects
A constructor expression with CAST followed by -> is an LHS-expression, you can assign values to it.
TYPES: BEGIN OF t_struc,
col1 TYPE i,
col2 TYPE i,
END OF t_struc.
DATA dref TYPE REF TO data.
DATA struc TYPE t_struc.
dref = NEW t_struc( ).
CAST t_struc( dref )->col1 = struc–col1.
While you're blogging about all the nice new features in ABAP 7.40 - is there any chance you could smuggle in a `... IS INSTANCE OF ...` operator? As I've blogged some time ago, this is one small change (at least from the outside) that would really help a lot...
I can ask the developers about that.
For the time being I myself also work like you have described it in your blog (using CAST from 7.40 on).
TRY.
CAST class( oref ).
...
CATCH cx_sy_move_cast_error.
ENDTRY.
Tbh i was coding a (super)class yesterday & share Volker's desperation for IS INSTANCE OF 😉 There is no simple way of doing this in ABAP, sigh 🙁
Horst Keller - Judging by the looks of it, i can see the demise of helper variables. Btw one thing came to my mind (in fact i had this in the back of my mind for sometime now, basically since i started chaining my methods 🙂 )
We have a functional method:
Suppose i have to call this method multiple times in my method (for some weird reason), what do we suggest we should do -
BR,
Suhas
The recommendation is absolutely clear for such cases:
2. Declare a helper variable & use it
Unfortunately the compiler does not recognize and buffer such intermediate results.
Use expressions to:
Jeez, I even found my guideline for that 😕
http://help.sap.com/abapdocu_731/en/index.htm?url=abenexpression_guidl.htm
I was wondering if the Kernel was smart enough to buffer the results 😛
I generally refer to the ABAP Programming guidelines (http://help.sap.com/abapdocu_731/en/abenabap_pgl.htm), in fact this 1 liner is a gem :
- Suhas
It probably will never be because it can't determine whether the return value might have changed. Think, for example, of an iterator, where invoking the next() method will actually change the state of the iterator so that consecutive calls of next() always return a different result - you wouldn't want the kernel to buffer that, would you?
Doesn't that hurt? 🙂
Darn!!!!
Should have remembered about the iterator before commenting, my bad! 😐
Horst Keller - What prompted you to quote John Keats in ABAP Keyword documentation? #needtoknow 😉
Cheers,
Suhas
Did I?
Sorry, it's not Keats.
It's right there at the top of the ABAP programming guidelines 😛 http://help.sap.com/abapdocu_731/en/abenabap_pgl.htm
It was the SAP-internal motto for the project that resulted in the ABAP PGLs.
PS: The correct answer of thread Object problem can be rewritten as follows in release 7.40:
DATA oref TYPE REF TO object.
oref = NEW some_class( ).
IF CAST cl_abap_classdescr(
cl_abap_typedescr=>describe_by_name( 'some_class' )
)->applies_to( oref ) = 'X'.
...
ENDIF.
At least one expression without helper variables ...
I asked, and the answer goes as as follows:
http://www.javapractices.com/topic/TopicAction.do?Id=31
😀
Does that mean that just because some people don't know how to use a language construct it's not going to be implemented? I can think of tons of ways to create bad examples for all the additions you blogged about. In contrast, there are valid uses of an 'instance of' operator - "if the class has implemented an interface to support some special handling, call the method of that interface, else use a generic method". You don't always have the option of changing the inheritance structure unless your language supports multiple inheritance - which C++ does, but neither Java nor ABAP do. So that's not much of an answer, to be honest.
Well, it's the opinion of the colleagues who design and implement all that stuff. I myself have use cases for INSTANCE OF too (especially in connection with RTTI) and I also use
TRY.
CAST class( oref ).
...
CATCH cx_sy_move_cast_error.
ENDTRY.
I would have expected that the integer division happens first and the resulting integer zero is then converted to float.
I guess my expectation is even more evident if I add some brackets:
data(a) = conv decfloat34( ( 1 / 3 ) ).
But a will be 0.33333..
Could you explain this behavior to me please?
Hi Bernhard,
In ABAP, arithmetic calculations have a calculation type that is derived not only from the operands at the RHS but also from the result type (LHS). The result type in your case is decfloat34 and therefore, the calculation is carried out in decfloat34.
See also Calculation Type, where the observed behavior is explained.
Thanks for the infos.
Do you know how to get the CONV to work with a CHANGING parameter?
the example here
ABAP Keyword Documentation
works for importing but not changing
e.g. I cannot activate the * commented lines below - see Syntax-Error
>>
class lcl_conv1 definition.
public section.
types tt_sta
type standard table of scarr with key mandt carrid.
methods import
importing
!II type LCL_CONV1=>TT_STA
changing
!CI type LCL_CONV1=>TT_STA optional
.
private section.
methods test.
endclass. " lcl_conv1 definition.
class lcl_conv1 implementation.
method IMPORT.
endmethod. " IMPORT.
method TEST.
data li type sorted table of scarr with unique key mandt carrid.
me->IMPORT( conv #( li ) ).
me->IMPORT( exporting II = conv #( li ) ).
* me->IMPORT(
* exporting
* II = conv #( li )
* changing
* CI = conv #( li ) "Syntax-Error: Unexpected operator "CONV"
* ).
endmethod. " TEST.
endclass. " lcl_conv1 implementation.
<<
TIA
Let me play the senior teacher here: 😈
How do you think could any expression work as an actual parameter for CHANGING?
Hint: Why can't you use expressions for EXPORTING/RETURNING parameters or use them as target fields of any assignments?
Actually, I'm whining about having to always copy my internal tables to a standard table just to display them, because salv takes a standard table only;- so I thought CONV would be a relief for that:
>>
class lcl_conv2 definition.
public section.
types tt_sta
type standard table of scarr with key mandt carrid.
methods show
changing
!CI type LCL_CONV2=>TT_STA
.
methods test.
endclass. " lcl_conv2 definition.
class lcl_conv2 implementation.
method show.
data lo type ref to CL_SALV_TABLE.
cl_salv_table=>FACTORY(
importing
R_SALV_TABLE = lo
changing
t_table = CI
).
lo->DISPLAY( ).
endmethod. " show.
method TEST.
data li type sorted table of scarr with unique key mandt carrid.
data li_helper type LCL_CONV2=>TT_STA.
select * from scarr into table li.
li_helper = li.
me->SHOW( changing CI = li_helper ).
endmethod.
endclass. " lcl_conv2 implementation.
<<
Well, you can never write to an expression (with some exceptions).
An expression returns a temporary result that can be used at the operand position, but not further. An actual parameter for CHANGING clearly is a write position. The changed parameter cannot be returned to the expression. You wouldn't write meth( CHANGING para = a + b ) either. Only some expressions are wirtable expressions: variants of NEW, CAST, and table expressions. Those can be used at some few write positions, but not with CHANGING.
Therefore, your problem cannot be solved by the compiler. I myself did never understand why the SALV classes do not accept any table type and why it is a CHANGING and not an IMPORTING parameter. But these classes are not part of ABAP language,.
yes yes yes.
I was hoping CONV would do something it doesn't.
I fully understand your point on Expressions,
Thanks much for your insights.
I guess I have an explanation for your "question" on SALV:
Class CL_SALV_TABLE has its method "FACTORY" with changing parameter t_table typed as "TYPE TABLE";
(i.e. standard table)
it then passes this to method "r_salv_table->set_data()" via parameter t_table typed as "TYPE STANDARD TABLE";
this method contains: "get reference of t_table into me->r_table"
where "me->r_table" is typed "Type Ref To DATA"
Class CL_GUI_ALV_GRID has its method "SET_TABLE_FOR_FIRST_DISPLAY"
with parameter IT_OUTTAB typed as "TYPE STANDARD TABLE";
and this method does a "get reference of it_outtab into mt_outtab."
where mt_outtab is typed "Type Ref To DATA"
So these both eventually pass into variables that would take any table.
BUT, checking for usage of e.g. the ladder mt_outtab, this gives e.g.:
Class CL_GUI_ALV_GRID with method "APPLY_FILTER":
This method contains: "assign mt_outtab->* to <tab1>." where <tab1> is typed "type standard table."
This calls function module "LVC_FILTER_APPLY" which has parameter tables "it_data" which is typed "TABLE"
(i.e. standard table)
and so on...
So this would required re-typing all these and more usages.
But there is more:
Hashed tables do not administrate sy-tabix within a Loop.
e.g. look at: "form filt_conv_fcat_to_lvcfilter"
in function group: SLVC_DIALOG
(include = LSLVC_DIALOGF18)
It contains:
>>
loop at t_data[] into t_data.
if sy-tabix = 1.
<<
where t_data is "tables t_data type table"
Now, if t_date was "any table", and then if we pass a hashed table, then sy-tabix would always be 0.
Like for example here:
sy-tabix is 0 (it's not a bug, it's a feature?)
>>
form z_hashed_sy_tabix.
data li type hashed table of scarr with unique key mandt carrid.
data li2 type sorted table of scarr with unique key mandt carrid.
field-symbols <lfs> like line of li.
select * from scarr into table li.
sort li by mandt carrid.
sy-tabix = -1.
LOOP at li assigning <lfs>.
write: /'sy-tabix=', (2) sy-tabix, <lfs>-CARRID.
ENDLOOP.
endform. " z_hashed_sy_tabix.
<<
So "sy-tabix = 1" cannot be used to find the first line with a hashed table.
Yet, even if the typing was merely changed to "index table",
that would allow for standard AND sorted tables.
http://help.sap.com/saphelp_nw2004s/helpdata/en/fc/eb366d358411d1829f0000e829fbfe/content.htm
As things are, I'll keep copying my tables;
at least this does NOT copy the body of the tables, because copy-on-write happens (I guess)
http://help.sap.com/saphelp_snc700_ehp02/helpdata/en/88/84fb5269d34838a1f119b41dcdbc57/content.htm
So "only" a table header needs to be created (?)
see table sharing
Hi Armin,
for me this is a good example of unreadable code.
data li
data li2
field-symbols <lfs> like line of li.
If you do not use meaningful names for IDs here, where do you?
Regards
Clemens
Great work! Nice features!
Where will I find these articles the day my customer uses 740? If I understand right, some of the features have been introduced with different service packs of 740. Is there an overview for this?
Thank you, I enjoy reading the articles and the discussion - really encouraging!
And: "Beauty Is Not in the Eye of the Beholder. Beauty Is All Around Us, Contained in Nature!"
See the News in the ABAP Documentation.
Seems, that CONV does not work in CONCATENATE:
data: stringfield type string.
concatenate conv string( sy-tabix ) 'anythingelse' into stringfield.
Could you tell me, why?
stringfield = CONV string( sy-tabix ) && 'anythingelse.' works, however. (As does stringfield = sy-tabix && 'anythingelse'.)
You still use CONCATENATE? :-O
Yes, I did - until now. 😉 Thanks!
Additonal Question: stringfield = sy-tabix && 'anythingelse' results in an output without a space (15anythingelse) - there is no pendant of "separated by...."?
stringfield = sy-tabix && | | && 'anythingelse'.
Of course, only use | delimiter for character data that shouldn't be translatable.
Does not work
are you sure? Shouldn't it be
|sy-tabix| && 'anythingelse'
You need the space?
` anythingelse` should do.
I concur.
Note && is not required: |{ sy-tabix } anythingelse|
No
|sy-tabix| will give you the string literal sy-tabix
As Jacques has shown, you use curly brackets to embed ABAP data objects or expressions into string templates. |{ sy-tabix }| converts the numeric contents of sy-tabix to string. |{ ... } { ... }| concatenates the contents of the expressions in the brackets separated by space.
Thanks. Learning every day.
After reading a few answers to my question, I asked myself, why the godfather of ABAP (TM) himself does not answer. Thanks for helping me!
Perhaps it's time for an updated ABAP Reference Book @galileo
Who is TM?
Arrgh, I'm pushing the deadlines of the new book again and again and really feel very bad about it (only a few chapters are finished), no reference bokk (that you find online) but a handbook ...
Ok guys. Now's the time to pile on the guilt and pressure. 😉
TM = ™ = Trademark.
"TM" is also used without a legal context: "The term trademark is also used informally to refer to any distinguishing attribute by which an individual is readily identified, such as the well-known characteristics of celebrities." (Trademark - Wikipedia, the free encyclopedia). Or in german: "Der Inbegriff"
The individual readily identified was here
Absolutely no ABAP for 15 days ...
Sehr schön 😛
See Matthew's answer. Expressions and new functions are supported mainly in expressions, not in old style ABAP.
Hi Horst,
given following example of the CAST operator:
TYPES t_c_big TYPE c LENGTH 262143.
DATA lv_char1 TYPE char1 VALUE '@'.
DATA lr_data_1 TYPE REF TO data.
GET REFERENCE OF lv_char1 INTO lr_data_1.
DATA(lr_data_2) = CAST t_c_big( lr_data_1 ).
Why do I get access to foreign memory in LR_DATA_2 after the first character? Is this a bug in the CAST operator?
I would have expected an exception due to some kind of memory check.
Kernel: 742 PL#200
SAP_BASIS: SAPKB74011
Thank you in advance!
Best regards,
Sebastian
Now, that doesn't look good 😯 .
According to the documentation the down cast should not be possible but lead to an exception CX_SY_MOVE_CAST_ERROR.
It is not the CAST operator alone, but also the ?= operator behaves wrongly:
DATA lr_data_3 TYPE REF TO t_c_big.
lr_data_3 ?= lr_data_1.
I tested in 7.40, SP08 and 7.50 and as you said, there is no exception and after assigning to a field symbol there is access to memory outside the allowed limits.
Only in our internal 7.63 system I get at least a runtime error CAST_INTERNAL_ERROR, which also shouldn't occur, but of course the CX_SY_MOVE_CAST_ERROR.
I forward to development. Thanks for notifying!
Hello Horst,
dereferencing a CASTed object attribute seems to add an implicit conversion, or what is happening here?
JNN
To make a long thing short:
One of the many peculiarities of our good ol' ABAP language.
As a data object, the operand must be character like (character-like data types or date types and time types, structures with flat-only character-like components),
As an expression it is enough to be convertible.
Can't we use CONV #( ) constructor for passing parameters while calling Function Modules?
because i am getting below error while calling FM
regards,
B@lu.
Please note that a function module call other than a method call is always dynamic. You specify the function module as a character field and not as a direct operator. Using a literal is kind of static but not for the compiler. It is evaluated a runtime only. Only the extended program check and the where-used list evaluate the literals.