Skip to Content
Technical Articles
Author's profile photo Jan Dahl

Pass dynamic table as reference out of method

Ever wondered how to get out your inline created dynamic table out of your method / sub procedure?

As known, only references can be passed through a method interface. But I failed always, because the referred table was empty outside of the method (freed stack) because it is created on stack memory which is only valid inside of the method scope.

So I managed to dynamically create a table of the same structure using RTTS and copied the content of the table in there. This table can be assigned to a class attribute reference without loosing the content. Why is ABAP that laborious?

Additionally I’ve only the select statement as declaration of my data to process in the report. If the customer needs an additional field even from an additional table, no problem, just add it to the select statement. That’s it!

REPORT zzjd_tmp2.

CLASS lc_flights DEFINITION.
  PUBLIC SECTION.
    METHODS main.
    METHODS get_data RETURNING VALUE(rr_dyntab) TYPE REF TO data.
    METHODS display_alv IMPORTING REFERENCE(ir_dyntab) type REF TO data.

  PRIVATE SECTION.
    DATA _r_datatable TYPE REF TO data.
ENDCLASS.
CLASS lc_flights IMPLEMENTATION.
  METHOD get_data.
*   create a fancy dynamic table
    SELECT FROM sflights AS f
      JOIN scarr AS ON c~carrid = f~carrid
      FIELDS c~*, f~*
      INTO TABLE @DATA(lt_flight).

*** copy table to heap memory ***
*   Create table description
    DATA(lo_tabledesc) = CAST cl_abap_tabledescr(
                           cl_abap_tabledescr=>describe_by_data( p_data = lt_flight ) ).
*   Create table in heap memory and set return reference
    CREATE DATA rr_dyntab TYPE HANDLE lo_tabledesc.

    FIELD-SYMBOLS <lt_datatable> LIKE lt_flight. " Create a field-symbol...
    ASSIGN rr_dyntab->TO <lt_datatable>.    " because append doesn't work with references
    APPEND LINES OF lt_flight TO <lt_datatable>. " Copy content to heap table
*********************************
  ENDMETHOD.
  METHOD display_alv.
    FIELD-SYMBOLS <lt_datatable> TYPE ANY TABLE.
    ASSIGN ir_dyntab->TO <lt_datatable>.

    cl_salv_table=>factory(
      IMPORTING r_salv_table   = DATA(lo_salv)
      CHANGING  t_table        = <lt_datatable>
    ).
    lo_salv->display( ).
  ENDMETHOD.
  METHOD main.
    _r_datatable = get_data( ).
    display_alv( _r_datatable ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA(lo_flights) = NEW lc_flights( ).
  lo_flights->main( ). 

Is there any more simple solution for that? Please let me know in the comments.

Please follow my profile.
Read also:

Assigned Tags

      25 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Sandra Rossi
      Sandra Rossi

      Based on the title of your blog post, I thought you would be explaining how to have a method return a dynamically-created internal table, and how to use it, i.e. some code like that:

      METHODS get_dynamic_itab
        RETURNING
          VALUE(ref_to_dynamic_itab) TYPE REF TO DATA.
      
      ...
      
      METHOD get_dynamic_itab.
        ...
        CREATE DATA ref_to_dynamic_itab ...
      ENDMETHOD.
      Author's profile photo Jan Dahl
      Jan Dahl
      Blog Post Author

      Yes, you are absolutely right. I changed my demo report. Now it is even better to understand.

      Author's profile photo Shai Sinai
      Shai Sinai
      1. I didn't understand what your technical issue is.
      2. You've posted a blog post instead of a question.
      Author's profile photo Jan Dahl
      Jan Dahl
      Blog Post Author

      Hi Shai, yes, this is a blog post not a question.

      How did you come up with it?

      Jan

      Author's profile photo Shai Sinai
      Shai Sinai

      I must confess you've been able to confuse me 🙂

      It might be because you don't really explain what problem you are trying to solve.

      (And you end your post with the question "Is there any more simple solution for that? Please let me know in the comments.")

      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      Judging by the comments, it works much better to post questions as blog posts. There would never be that many replies from such esteemed Community members if this was a question. 🙂

      Author's profile photo Andrea Borgia
      Andrea Borgia

      People love correcting more than aswering 😉

      ( saw this quip somewhere 😀 )

      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      I had similar expectation to Sandra's, based on the title.

      It says "only references can be passed through a method interface" - that's not true, RETURNING parameters are always "by value", for example. But then in the provided example the methods don't have any parameters at all. And you're trying to do... what? Get access to the internal table that is local to one method from another method? That should never be needed. Whatever the method intends to pass along to others, should be declared as a parameter. If it's not intended to be accessed from the outside then just don't.

      Rather confused by this post, to be honest...

      Author's profile photo Jan Dahl
      Jan Dahl
      Blog Post Author

      Thanks for your replay, I changed it 🙂 Please tell me if you like it better now.

      Author's profile photo Juwin Pallipat Thomas
      Juwin Pallipat Thomas

      In addition to the passing comments from Jelena Perfiljeva & Sandra Rossi

      The blog title says, Pass by reference. Pass by reference also relates to saving memory used by programs.

      But the following code

      APPEND LINES OF lt_flight TO <lt_datatable>.

      is actually duplicating the data into another table, there by doubling the memory requirement.

      Author's profile photo Jan Dahl
      Jan Dahl
      Blog Post Author

      The dynamic table of the select statement will be freed as soon as the method is left. this was my root problem.

      Author's profile photo Juwin Pallipat Thomas
      Juwin Pallipat Thomas

      That should be addressed in other ways. Data duplication isn't the correct solution for that.

      Author's profile photo Enno Wulff
      Enno Wulff

      Jan Dahl This blog post really is a bit confusing because you do not describe the problem that this technique solves...

      In this special case you do not need to use RTTC functions.

      Creating a data reference based on an existing internal table is as easy as this:

      CREATE DATA _r_datatable LIKE lt_flights.

      Jelena Perfiljeva says:

      And you're trying to do... what? Get access to the internal table that is local to one method from another method? That should never be needed.

      That's the point where Jelena is absolutely right: If you know the database table there is no need to do this dynamically!

      But: I wouldn't say this will never be needed. The following program select data dynamically into a locally defined table and displays the values in another method. It is also demonstrated how to access the data from outside the class.

      REPORT.
      
      PARAMETERS p_table TYPE tablename DEFAULT 'T006A'.
      
      START-OF-SELECTION.
      
      CLASS main DEFINITION.
        PUBLIC SECTION.
          METHODS constructor
            IMPORTING
              tabname TYPE clike.
          METHODS read.
          METHODS show.
          METHODS get_ref
            RETURNING
              VALUE(result) TYPE REF TO data.
      
        PRIVATE SECTION.
          DATA datref TYPE REF TO data.
          DATA tabname TYPE string.
      
      ENDCLASS.
      
      CLASS main IMPLEMENTATION.
        METHOD constructor.
          me->tabname = tabname.
        ENDMETHOD.
        METHOD read.
      
          DATA(structdescr) = cl_abap_structdescr=>describe_by_name( p_name = tabname ) .
          DATA(tabledescr) = cl_abap_tabledescr=>create(
                               p_line_type  = CAST #( structdescr ) ).
          CREATE DATA datref TYPE HANDLE tabledescr.
          FIELD-SYMBOLS: <tabdata> TYPE STANDARD TABLE.
          ASSIGN datref->* TO <tabdata>.
      
      
          SELECT * FROM (tabname) INTO TABLE @<tabdata>.
      
        ENDMETHOD.
      
        METHOD get_ref.
          result = datref.
        ENDMETHOD.
      
        METHOD show.
      
          FIELD-SYMBOLS: <tabdata> TYPE STANDARD TABLE.
          ASSIGN datref->* TO <tabdata>.
      
          cl_demo_output=>display_data( name = 'Inside class' value = <tabdata> ).
        ENDMETHOD.
      
      ENDCLASS.
      
      
      START-OF-SELECTION.
      
        DATA(appl) = NEW main( p_table ).
        appl->read( ).
        appl->show( ).
      
        DATA(datref) = appl->get_ref( ).
        FIELD-SYMBOLS <tabdata> TYPE ANY TABLE.
        ASSIGN datref->* TO <tabdata>.
      
        cl_demo_output=>display_data( name = 'outside class' value = <tabdata> ).

       

      Author's profile photo Jan Dahl
      Jan Dahl
      Blog Post Author

      Yes, you are right. Nice improvement to my solution. It points out the "passing reference" aspect very good. In the mean while made a second version my self. The reason for this was to have a report, where the select statement is the only point of declaration for the data to process. I'm so tiered to change several places if the customer needs a field more or less.

      Author's profile photo Christian Guenter
      Christian Guenter

      In newer ABAP releases this can even more simplified.

      *&---------------------------------------------------------------------*
      *& Report ztest_dyn
      *&---------------------------------------------------------------------*
      *&
      *&---------------------------------------------------------------------*
      REPORT ztest_dyn.
      
      PARAMETERS p_table TYPE tablename DEFAULT 'T006A'.
      
      START-OF-SELECTION.
      
      CLASS main DEFINITION.
        PUBLIC SECTION.
          METHODS constructor
            IMPORTING
              tabname TYPE clike.
          METHODS read.
          METHODS show.
          METHODS get_ref
            RETURNING
              VALUE(result) TYPE REF TO data.
      
        PRIVATE SECTION.
          DATA datref TYPE REF TO data.
          DATA tabname TYPE string.
      
      ENDCLASS.
      
      CLASS main IMPLEMENTATION.
        METHOD constructor.
          me->tabname = tabname.
        ENDMETHOD.
      
        METHOD read.
          SELECT * FROM (tabname) INTO TABLE NEW @datref.
        ENDMETHOD.
      
        METHOD get_ref.
          result = datref.
        ENDMETHOD.
      
        METHOD show.
          cl_demo_output=>display_data( name = 'Inside class' value = datref->* ).
        ENDMETHOD.
      ENDCLASS.
      
      
      START-OF-SELECTION.
      
        DATA(appl) = NEW main( p_table ).
        appl->read( ).
        appl->show( ).
      
        DATA(datref) = appl->get_ref( ).
        cl_demo_output=>display_data( name = 'outside class' value = datref->* ).
      
      Author's profile photo Enno Wulff
      Enno Wulff

      ❤️

      Author's profile photo Jan Dahl
      Jan Dahl
      Blog Post Author
      SELECT * FROM (tabname) INTO TABLE NEW @datref.

      That's exactly what I'm looking for. Do you know the release no. of that?

       

      Author's profile photo Christian Guenter
      Christian Guenter

      NW >= 754 I think
      https://help.sap.com/doc/abapdocu_754_index_htm/7.54/en-US/abapselect_into_target.htm

      Author's profile photo Alexander Laible
      Alexander Laible

      Also possible:

      CREATE DATA dataref TYPE TABLE OF (tabname) WITH EMPTY KEY.
      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      Can't believe I'm arguing with The Great Enno but isn't this a violation of some kind of programming principle? "Open-closed" or something? This seems like one of 'em "even though you can, it doesn't mean you should" things, sorry. 🙂

      Author's profile photo Enno Wulff
      Enno Wulff

      Hey Jelena Perfiljeva you wrote:

      This seems like one of 'em "even though you can, it doesn't mean you should" things, sorry. 🙂

      That's a different pair of shoes... 😀

      Jan Dahl explained:

      The reason for this was to have a report, where the select statement is the only point of declaration for the data to process. I'm so tiered to change several places if the customer needs a field more or less.

      Depending on what to do with the data it sounds totally legit to me to choose this way of programming. If there were a lot of data manipulations inside this class, then this would require dynamic access. To avoid dynamic access where it isn't necessarily needed, I would rather choose a static data declaration.

      To evade the annoying process of adding new fields I'd define a static structure and used SELECT INTO CORRESPONDING FIELDS.

      Author's profile photo Jan Dahl
      Jan Dahl
      Blog Post Author

      To evade the annoying process of adding new fields I'd define a static structure and used SELECT INTO CORRESPONDING FIELDS.

      Yes, me too, but then you loose the performance advantage, right? To have the flexibility from above with "INTO CORRESPONDING FIELDS" you have to order all fields from each table in the select. I guess the "INTO CORRESPONDING FIELDS" is done on the Application-Server? Here you discard all the data provided from the database, which is not in your static structure. Would be interesting to do a measurement here.

      Author's profile photo Enno Wulff
      Enno Wulff

      It's a long time ago since INTO CORRESPONDING FIELDS was a perfomance issue. Don't ask me for a version...

      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      It's amazing how long those urban legends live! 🙂

      Author's profile photo Paul Hardy
      Paul Hardy

      Probably once upon a time, ten or more years ago, you would have wanted to avoid INTO CORRESPONDING FIELDS due to performance considerations. But to be honest I have been doing this (INTO CORRESPONDNG) since 1997 and have never had a problem even with bucket loads of fields.

      So I suspect this was never a problem.

      And the Hungarian Notation is a bit odd as well. What does the underscore at the front of a variable mean?

      Cheersy Cheers

      Paul