Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Type conflict in a dynamic method call

yuriy_dzhenyeyev
Explorer
0 Kudos

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?

1 ACCEPTED SOLUTION

yuriy_dzhenyeyev
Explorer

...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

4 REPLIES 4

yuriy_dzhenyeyev
Explorer

...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

Sandra_Rossi
Active Contributor

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.

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.

gabmarian Thanks, answer edited.