07-28-2022 9:35 AM
Hello World,
...there's something about the dynamic method call that I dont understand. Here's the setup:
zcl-phrase.txt - the only purpose of this class is to hold a string in a public attribute PHRASE and provide a wrapper method for test (3) - see below.
zcl-output.txt - takes the instance of ZCL_PHRASE and writes its PHRASE attribute.
zsandbox5.txt - the test report.
My intention is to have a class that would "save the dynamic method call for later", something like this: zcl-dynamic-call.txt
Now, in the test report, this is a direct call:
START-OF-SELECTION.
DATA instance TYPE REF TO zcl_phrase.
instance = NEW zcl_phrase( 'Hello world' ).
*******************
* (1) direct call *
*******************
zcl_output=>say_phrase( instance ). "prints "Hello World
This dynamic call works OK:
********************
* (2) dynamic call *
********************
DATA params TYPE abap_parmbind_tab.
params = VALUE #( (
kind = 'E'
name = 'INSTANCE'
value = REF #( instance ) ) ). "<< wrap class reference as REF TO DATA
CALL METHOD ('ZCL_OUTPUT')=>('SAY_PHRASE') PARAMETER-TABLE params. "prints 'Hello World'
At this stage, here's how our params look like:
As I want to specify my dynamic call parameters "elsewhere", my first idea was simply to do the same:
***************************************************************
* (3) prepare dynamic call 'elsewhere' - same approach as (2) *
***************************************************************
params = instance->get_parmbind( ).
* CALL METHOD ('ZCL_OUTPUT')=>('SAY_PHRASE') PARAMETER-TABLE params. "crashes because abap_parmbind-value is FREED STACK
Alas, REF# ... exists only within stack, so it is invalidated immediately:
Now in ZCL_DYNAMIC_CALL, the object reference is stored unchanged, it is wrapped into REF immediately before making the dynamic call.
*******************************************************************************
* (4) same idea, but create REF #( instance ) immediately before dynamic call *
*******************************************************************************
DATA(dynamic_call) = NEW zcl_dynamic_call(
method_to_call = 'SAY_PHRASE'
class_name = 'ZCL_OUTPUT'
oref_params = VALUE #( ( name = 'INSTANCE' kind = 'E' oref = instance ) )
).
dynamic_call->execute( ). "Crashes with 'Type conflict in a dynamic method call.'
the parameter table looks good
...but the call results in a short dump:
I cannot tell the difference between the call in (2) and (4). The parameter table looks exactly the same, and still (4) crashes and (2) does not.
Any ideas?
07-28-2022 10:45 AM
...OK, got it.
zcl_dynamic_call defines its parameter table as follows:
types:
BEGIN OF oref_parmbind,
name TYPE abap_parmname,
kind TYPE abap_parmkind,
oref TYPE REF TO object,
END OF oref_parmbind .
types:
oref_parmbind_tab TYPE HASHED TABLE OF oref_parmbind
WITH UNIQUE KEY name .
so as soon as I store my zcl_phrase instance here, it becomes TYPE REF TO OBJECT resulting in the type conflict.
To use it in a dynamic call, we must restore the right type:
WRONG:
METHOD execute.
DATA tmp_params TYPE abap_parmbind_tab.
LOOP AT oref_params INTO DATA(oref_param).
DATA(new_line) = CORRESPONDING abap_parmbind( oref_param ).
new_line-value = REF #( oref_param-oref ).
INSERT new_line INTO TABLE tmp_params.
ENDLOOP.
CALL METHOD (class_name)=>(method_to_call) PARAMETER-TABLE tmp_params.
ENDMETHOD.
CORRECT:
METHOD execute.
DATA tmp_params TYPE abap_parmbind_tab.
LOOP AT oref_params INTO DATA(oref_param).
DATA(new_line) = CORRESPONDING abap_parmbind( oref_param ).
DATA(rto) = oref_param-oref.
* rto's type is REF TO OBJECT, now we must help it to remember its original type
* otherwise the dynamic call will crash
DATA(absolute_name) = cl_abap_classdescr=>describe_by_object_ref( rto )->absolute_name. "rto's original type
DATA rtd TYPE REF TO data.
CREATE DATA rtd TYPE REF TO (absolute_name).
ASSIGN rtd->* TO FIELD-SYMBOL(<fs>).
<fs> ?= rto. "casting to rto's original type
new_line-value = rtd.
INSERT new_line INTO TABLE tmp_params.
ENDLOOP.
CALL METHOD (class_name)=>(method_to_call) PARAMETER-TABLE tmp_params.
ENDMETHOD.
BR,
Yuriy
07-28-2022 10:45 AM
...OK, got it.
zcl_dynamic_call defines its parameter table as follows:
types:
BEGIN OF oref_parmbind,
name TYPE abap_parmname,
kind TYPE abap_parmkind,
oref TYPE REF TO object,
END OF oref_parmbind .
types:
oref_parmbind_tab TYPE HASHED TABLE OF oref_parmbind
WITH UNIQUE KEY name .
so as soon as I store my zcl_phrase instance here, it becomes TYPE REF TO OBJECT resulting in the type conflict.
To use it in a dynamic call, we must restore the right type:
WRONG:
METHOD execute.
DATA tmp_params TYPE abap_parmbind_tab.
LOOP AT oref_params INTO DATA(oref_param).
DATA(new_line) = CORRESPONDING abap_parmbind( oref_param ).
new_line-value = REF #( oref_param-oref ).
INSERT new_line INTO TABLE tmp_params.
ENDLOOP.
CALL METHOD (class_name)=>(method_to_call) PARAMETER-TABLE tmp_params.
ENDMETHOD.
CORRECT:
METHOD execute.
DATA tmp_params TYPE abap_parmbind_tab.
LOOP AT oref_params INTO DATA(oref_param).
DATA(new_line) = CORRESPONDING abap_parmbind( oref_param ).
DATA(rto) = oref_param-oref.
* rto's type is REF TO OBJECT, now we must help it to remember its original type
* otherwise the dynamic call will crash
DATA(absolute_name) = cl_abap_classdescr=>describe_by_object_ref( rto )->absolute_name. "rto's original type
DATA rtd TYPE REF TO data.
CREATE DATA rtd TYPE REF TO (absolute_name).
ASSIGN rtd->* TO FIELD-SYMBOL(<fs>).
<fs> ?= rto. "casting to rto's original type
new_line-value = rtd.
INSERT new_line INTO TABLE tmp_params.
ENDLOOP.
CALL METHOD (class_name)=>(method_to_call) PARAMETER-TABLE tmp_params.
ENDMETHOD.
BR,
Yuriy
07-28-2022 12:27 PM
EDIT: whole answer corrected.
Dynamic calling works the same as static calling. Below shows what is accepted/not accepted:
CLASS lcl_app DEFINITION.
PUBLIC SECTION.
CLASS-METHODS method
IMPORTING
object TYPE REF TO if_ixml.
ENDCLASS.
CLASS lcl_app IMPLEMENTATION.
METHOD method.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA object TYPE REF TO object.
DATA(ixml) = cl_ixml=>create( ).
object = ixml.
" static calling FAILS
TRY.
* lcl_app=>method( object = object ). "<==== doesn't compile
CATCH cx_root INTO DATA(error).
MESSAGE error TYPE 'I'.
ENDTRY.
" static calling SUCCEEDS
lcl_app=>method( object = ixml ).
" dynamic calling FAILS
DATA(parameters) = VALUE abap_parmbind_tab(
( name = 'OBJECT'
kind = 'E'
value = REF #( object ) ) ).
TRY.
CALL METHOD ('LCL_APP')=>method PARAMETER-TABLE parameters. "<==== fails
CATCH cx_root INTO DATA(error).
MESSAGE error TYPE 'I'.
ENDTRY.
" dynamic calling SUCCEEDS
parameters = VALUE abap_parmbind_tab(
( name = 'OBJECT'
kind = 'E'
value = REF #( ixml ) ) ).
CALL METHOD ('LCL_APP')=>method PARAMETER-TABLE parameters.
07-28-2022 1:21 PM
The problem with OP's code that this statement within a method call
new_line-value = REF #( oref_param-oref ).
creates a reference variable that points to an object reference with type TYPE REF TO object.
The parameters table expects a properly typed object reference variable, which in this case is TYPE REF TO zcl_phrase.
07-28-2022 1:55 PM