Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
jan_dahl
Explorer

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:
25 Comments
Sandra_Rossi
Active Contributor
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.
shais
Participant

  1. I didn't understand what your technical issue is.

  2. You've posted a blog post instead of a question.

Jelena
Active Contributor

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

Juwin
Active Contributor
In addition to the passing comments from jelena.perfiljeva2 & 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.
ennowulff
Active Contributor
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.perfiljeva2 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> ).

 
jan_dahl
Explorer
Yes, you are absolutely right. I changed my demo report. Now it is even better to understand.
jan_dahl
Explorer
0 Kudos
Hi Shai, yes, this is a blog post not a question.

How did you come up with it?

Jan
jan_dahl
Explorer
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.
jan_dahl
Explorer
0 Kudos
Thanks for your replay, I changed it 🙂 Please tell me if you like it better now.
jan_dahl
Explorer
0 Kudos
The dynamic table of the select statement will be freed as soon as the method is left. this was my root problem.
ChristianGünter
Contributor
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->* ).
ennowulff
Active Contributor
0 Kudos
❤️
Juwin
Active Contributor
0 Kudos
That should be addressed in other ways. Data duplication isn't the correct solution for that.
al_01
Explorer
Also possible:
CREATE DATA dataref TYPE TABLE OF (tabname) WITH EMPTY KEY.
shais
Participant
0 Kudos
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.")
Jelena
Active Contributor
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. 🙂
Jelena
Active Contributor
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. 🙂
abo
Active Contributor
0 Kudos
People love correcting more than aswering 😉

( saw this quip somewhere 😄 )
ennowulff
Active Contributor
Hey jelena.perfiljeva2 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.
jan_dahl
Explorer
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.
jan_dahl
Explorer
0 Kudos
SELECT * FROM (tabname) INTO TABLE NEW @datref.

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

 
ennowulff
Active Contributor
It's a long time ago since INTO CORRESPONDING FIELDS was a perfomance issue. Don't ask me for a version...
ChristianGünter
Contributor
Jelena
Active Contributor
It's amazing how long those urban legends live! 🙂
hardyp180
Active Contributor
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