I'd like to build a container ABAP class that wraps an arbitrary internal table.
My initial approach was to define a member variable of TYPE REF TO DATA and pass a reference into the constructor.
The problem is that due to the pointer the instance is still dependent on the original itab. So if the original table is freed from memory you cannot access the data anymore. I'd need to have a real copy of the table data stored within the object, so I would be able to pass the object outside the original scope of the itab.
Is there any way of achieving this in ABAP?
Sample code with references that crashes in the scenario defined in the end:
CLASS lcl_test_itab_wrapper DEFINITION LOCAL FINAL
CREATE PUBLIC.
PUBLIC SECTION.
CLASS-METHODS: access_outside_itab_scope.
METHODS: constructor IMPORTING itab TYPE table,
access_itab_data.
PRIVATE SECTION.
CLASS-METHODS: sample_itab_setup RETURNING VALUE(result) TYPE REF TO lcl_test_itab_wrapper.
DATA: table_ref TYPE REF TO data.
ENDCLASS.
CLASS lcl_test_itab_wrapper IMPLEMENTATION.
METHOD access_itab_data.
FIELD-SYMBOLS <table> TYPE table.
ASSIGN me->table_ref->* TO <table>.
WRITE:/ lines( <table> ).
ENDMETHOD.
METHOD constructor.
me->table_ref = REF #( itab ).
ENDMETHOD.
METHOD sample_itab_setup.
DATA: dummy_itab TYPE TABLE OF string.
APPEND 'test_record' TO dummy_itab.
CREATE OBJECT result EXPORTING itab = dummy_itab.
ENDMETHOD.
METHOD access_outside_itab_scope.
DATA(o_instance) = sample_itab_setup( ).
" Here it crashes as the referenced itab was freed already.
" I'd need to have a real itab copy stored in the instance
o_instance->access_itab_data( ).
ENDMETHOD.
Update: Solution based on #vwegert answer
Replace constructor reference assignment by:
CREATE DATA me->table_ref LIKE itab.
FIELD-SYMBOLS <table> TYPE table.
ASSIGN me->table_ref->* TO <table>.
<table> = itab.
You need to create a data object dynamically instead of (ab)using a statically defined one. Check the documentation of the CREATE DATA statement.
Check out the "GET REFERENCE of xx into xx" statement as well, you can save three lines of your code.
Related
I've written an ABAP Method, which returns me some analyses in a custom table.
Now I want to call this Method from an RFC module.
So far so good - the method works fine, but I'm curious of how to return this table?
Do I have to create a table / structure ... in SE11 to make it work because otherwise I can't refer to this table type or is there an easier way?
I'm quite new to ABAP so I don't know the best practices.
m_analyses = new zcl_manalyses( ).
data(lt_m_analyses) = m_analyses->analyse_m_data(
budat_from = budat_from
budat_to = budat_to
).
The TYPES statement can not only occur inside a method's body, but also inside the class definition, in which case it can be accessed from other places with class_name=>type_name (if it's public):
CLASS cl_user_registry DEFINITION PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF user,
firstname TYPE string,
lastname TYPE string,
END OF user,
users TYPE STANDARD TABLE OF user.
METHODS:
get_current_users
RETURNING users.
ENDCLASS.
DATA current_users TYPE cl_user_registry=>users.
current_users = cl_user_registry=>get_current_users( ).
You first have to create a structure in ABAP Dictionary (SE11), then you create a table type in SE11 as well.
You then reference the structure in the line type of the table type.
Try using the new global table type, it should work. (with typing 'TYPE')
I have 2 rows shown in the ALV list, one of this column has domain values.
If I click on the search help right it doesn't show any values at all.
Do I have to activate something in the class to see the values of any domain?
Automatic search help (aka domain values) will be showed only when creating ALV via Dictionary structure, and that's why it is impossible with cl_salv_table, because it accepts only internal table.
However, it have special method set_ddic_reference for assigning F4 values.
DATA: lr_column TYPE REF TO cl_salv_column_table,
lr_columns TYPE REF TO cl_salv_columns_table.
DATA: ls_ddic type salv_s_ddic_reference.
lr_columns = o_alv->get_columns( ).
lr_column ?= lr_columns->get_column( columnname = 'MANDT' ).
ls_ddic-table = 'T001'.
ls_ddic-field = 'MANDT'.
lr_column->set_ddic_reference( ls_ddic ).
lr_column->set_f4( abap_true ).
This code should be called after the factory constructor and before the display() method.
So these both seem to work for me:
TABLES:
T001, "Table of Company Codes.
Z_KNA1_VBRK. "View I created..
DATA:
CCNAME TYPE T001-BUTXT,
CCCURR TYPE T001-WAERS,
KNAVBK TYPE Z_KNA1_VBRK,
AMNICC TYPE Z_KNA1_VBRK-NETWR.
and
DATA:
CCNAME LIKE T001-BUTXT,
CCCURR LIKE T001-WAERS,
KNAVBK LIKE Z_KNA1_VBRK,
AMNICC LIKE Z_KNA1_VBRK-NETWR.
PARAMETERS:
COMPCODE LIKE T001-BUKRS.
Is there any difference between them technically? Which is preferred / best practice and why?
To get the difference try to compile the following program.
REPORT zzz.
CLASS lcl_main DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
main.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD main.
DATA: ls_t000t TYPE t000,
ls_t000l LIKE t000.
ENDMETHOD.
ENDCLASS.
The error message you will get is
Within classes and interfaces, you can only use "TYPE" to refer to ABAP Dictionary types, not "LIKE" or "STRUCTURE".
This is because in the OO context you need to write explicitly TYPE when you actually refer to a type. This is the current state of the art.
Now change your program slightly and try to declare global variables with LIKE and TYPE.
REPORT zzz.
DATA: gs_t000t TYPE t000,
gs_t000l LIKE t000.
CLASS lcl_main DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
main.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD main.
DATA: ls_t000t TYPE t000.
* ls_t000l LIKE t000.
ENDMETHOD.
ENDCLASS.
As you can see there are no compilation errors in this case. In this context TYPE and LIKE are interchangeable, they mean the same. This applies also to the "old" parts of ABAP means of modularization like subroutines and function modules.
However I use the following rule of thumb.
Whenever I refer to a DDIC or local type I use TYPE. If I want to create a variable that is exactly of the same type like other variable I use LIKE. Should the type of the original variable change in the future, the change has to be made only in one place then.
Example.
METHOD main.
DATA: ls_t000t TYPE t000. "should the type change from T000 to T002
"in the future, one has to change it only in one place.
DATA: ls_t000l LIKE ls_t000t.
ENDMETHOD.
Yes, there is a difference. You cannot get this difference because you declared your structures via TABLES statement, which is obsolete now and shouldn't be used.
TABLES statement declares interface work area with a name identical to Data Dictionary structure. Therefore both your LIKE and TYPE declarations treat Z_KNA1_VBRK either like data object or DDIC structure correspondingly.
In any other case such declaration won't compile because LIKE and TYPE statements are not interchangable.
You should declare separate structures in your program rather than using such obsolete elements. The only allowed exception is data exchange with classic dynpros.
To further details, switch to ABAP documentation.
when inspecting an object-instance in the debugger, it will be printed like this:
{O:9*CLASS=CL_SOMETHING}
Is it possible to retrieve that class' identity-number 9 from a given object reference?
I want to distinguish multiple instances of the same class and print their instance-number.
I found no way using the RTTI to get that information, any advice?
As far as I know, you can't access that internal object identifier. The debugger uses some private kernel interface to do so that is not accessible to the ordinary user. You could try something like this:
CLASS lcl_object_id_map DEFINITION.
PUBLIC SECTION.
METHODS get_id
IMPORTING ir_object TYPE REF TO object
RETURNING value(r_id) TYPE sysuuid_c.
PRIVATE SECTION.
TYPES: BEGIN OF t_object_id,
object TYPE REF TO object,
id TYPE sysuuid_c,
END OF t_object_id,
tt_object_id_map TYPE HASHED TABLE OF t_object_id
WITH UNIQUE KEY object.
DATA gt_object_id_map TYPE tt_object_id_map.
ENDCLASS. "lcl_object_id_map DEFINITION
*----------------------------------------------------------------------*
CLASS lcl_object_id_map IMPLEMENTATION.
METHOD get_id.
DATA: ls_entry TYPE t_object_id.
FIELD-SYMBOLS: <ls_entry> TYPE t_object_id.
READ TABLE gt_object_id_map
ASSIGNING <ls_entry>
WITH KEY object = ir_object.
IF sy-subrc <> 0.
ls_entry-object = ir_object.
ls_entry-id = cl_system_uuid=>create_uuid_c32_static( ).
INSERT ls_entry INTO TABLE gt_object_id_map ASSIGNING <ls_entry>.
ENDIF.
r_id = ls_entry-id.
ENDMETHOD. "get_id
ENDCLASS. "lcl_object_id_map IMPLEMENTATION
I actually found an (internal) way to get the object's internal ID in the Object Services CL_OS_CA_COMMON=>OS_GET_INTERNAL_OID_BY_REF:
CALL 'OBJMGR_GET_INFO' ID 'OPNAME' FIELD 'GET_OBJID'
ID 'OBJID' FIELD integer_oid
ID 'OBJ' FIELD ref_to_object.
Yes, this is internal stuff... Use at own risk.
I've declared a type with the type of ref to data. so it looks like this
my_type type ref to data.
Then I declare an internal table, which I want to assign to my_type.
Data:
ref_data type my_type.
itable type it_table.
ref_data = itable.
Why can't I assign itable to ref_data, isn't a ref to data is a generic data type and can be assigned to anything?
This is very similar to other programming languages, and it's not a problem of typing the variables or references. You're trying to assign a value to a pointer variable - that won't work anywhere. You need to use GET REFERENCE OF itable INTO ref_data.
That's not quite how a data reference works. A data reference has to be typed, but you type it at run time.
data: ref_data type ref to data.
data: itable type it_table.
"you access the data in a data reference via a field symbol
field-symbols: <dref> type any.
create data ref_data type it_table.
assign ref_data->* to <dref>.
<dref> = itable.
I now have a copy of itable in my dynamically typed variable ref_data, accessed by the field symbol .