You can use CASTING TYPE
construction for this task. Here is the sample solution based on RTTS:
DATA: handle TYPE REF TO data,
lref_struct TYPE REF TO cl_abap_structdescr.
SELECT DISTINCT * UP TO 100 ROWS
FROM cdpos
INTO TABLE @DATA(t_cdpos)
WHERE tabname NOT LIKE '/%'.
LOOP AT t_cdpos ASSIGNING FIELD-SYMBOL(<fs_cdpos>).
lref_struct ?= cl_abap_structdescr=>describe_by_name( <fs_cdpos>-tabname ).
* get key fields
DATA(key_fields) = VALUE ddfields( FOR line IN lref_struct->get_ddic_field_list( ) WHERE ( keyflag NE space ) ( line ) ).
* filling key field components
DATA(key_table) = VALUE abap_component_tab( FOR ls_key IN key_fields
( name = ls_key-fieldname
type = CAST #( cl_abap_datadescr=>describe_by_name( ls_key-domname ) )
)
).
* create key fields type handle
TRY.
DATA(r_type_struct) = cl_abap_structdescr=>create( key_table ).
CATCH cx_sy_struct_creation .
ENDTRY.
* create key type
CHECK r_type_struct IS NOT INITIAL.
CREATE DATA handle TYPE HANDLE r_type_struct.
ASSIGN handle->* TO FIELD-SYMBOL(<structure>).
* assigning final key structure
ASSIGN <fs_cdpos>-tabkey TO <structure> CASTING TYPE HANDLE r_type_struct.
ENDLOOP.
UPD: What concerns OP's question about addressing result structure, one cannot address its components by name (like WERKS), as one cannot know in advance its type, as it changes dynamically. You should either access structure components like:
ASSIGN COMPONENT 1 OF STRUCTURE <table> TO <component>.
Another, more robust variant will be using type handle r_type_struct
which contains all fields components[]
table:
LOOP AT r_type_struct->components[] ASSIGNING FIELD-SYMBOL(<fs_comp>).
ASSIGN COMPONENT <fs_comp>-name OF STRUCTURE <table> TO <component>.
IF <COMPONENT> IS ASSIGNED.
"do smth
ENDIF.
ENDLOOP.