使用循环将参数传递给宏
Passing parameters to macro with a loop
有一个用宏填充深层结构的任务,其中结构组件的名称相似,可以通过简单的索引循环构建。
比如结构为root-level1-level2-level3-level4
我想用下面的嵌套宏填充它
DEFINE iterate_menges.
do &4 times.
fill &1 &2 sy-index level4.
enddo.
END-OF-DEFINITION.
DEFINE fill.
cs_root-sheet&1-&2-level&3-&4 = 'some_value'.
END-OF-DEFINITION.
但是这个概念不起作用,sy-index
被当作文本处理。错误
component cs_root-sheet1-level2-levelsy-index-level4 is not found
已显示,但数字字面量效果非常好。
这里应该使用什么语法?
ADDITION: 这是我在 SCN 上找到的示例片段,它运行良好。为什么会这样?
DEFINE ADD_MAPPING.
p_c = &1.
CONDENSE p_c.
CONCATENATE 'p_old' p_c INTO p_c.
ASSIGN (p_c) TO <fs>.
WRITE <fs>.
END-OF-DEFINITION.
DO 14 TIMES.
ADD_MAPPING sy-index.
ENDDO.
P.S。是的,我知道宏是不可调试的、不安全的并且完全不应该使用,但我对这个特定问题感兴趣而不是最佳实践建议。
系统正在执行 documentation 中所述的操作。不幸的是,在这种情况下,英文翻译缺少一些细节,而不是德文原文,更重要的是,恕我直言。宏的使用不是某种调用,它是编译前发生的文本替换。参数被替换,而不是被评估 - 它们 不能 被评估,因为在大多数情况下,该值在编译时是未知的,仅在运行时是未知的。要做你想做的事,你将不得不使用像 ASSIGN COMPONENT ... OF ...
这样的动态访问技术
你想做的事是不可能的,因为宏只有在编译时才知道。它们不是 ABAP 中模块化手段的一部分。
人们甚至可以编写自己的 Brainfuck 版本。看下面的例子。
REPORT zzz.
TYPES: BEGIN OF t1,
sheet1 TYPE c,
sheet2 TYPE c,
END OF t1.
DATA:
cs_root TYPE t1.
DEFINE test.
cs_root-sheet&1 = 'costam'.
END-OF-DEFINITION.
DEFINE brainfuck.
&4=>&1&3 &2.
END-OF-DEFINITION.
START-OF-SELECTION.
brainfuck IF_SYSTEM_UUID_STATIC~CREATE_UUID_X16 ) ( CL_SYSTEM_UUID.
test sy-index.
在另一个答案下回答您的评论。解决方案可能如下所示。
REPORT zzz.
TYPES: BEGIN OF t4,
level4 TYPE c,
END OF t4.
TYPES: BEGIN OF t3,
level1 TYPE t4,
level2 TYPE t4,
END OF t3.
TYPES: BEGIN OF t2,
level1 TYPE t3,
level2 TYPE t3,
END OF t2.
TYPES: BEGIN OF t1,
sheet1 TYPE t2,
sheet2 TYPE t2,
END OF t1.
CLASS lcl_test DEFINITION FINAL.
PUBLIC SECTION.
CLASS-METHODS:
test
IMPORTING
i_1 TYPE i
i_2 TYPE i
i_3 TYPE i
CHANGING
cs_root TYPE t1.
ENDCLASS.
CLASS lcl_test IMPLEMENTATION.
METHOD test.
ASSIGN COMPONENT |sheet{ i_1 }| OF STRUCTURE cs_root TO FIELD-SYMBOL(<fs_sheet>).
IF sy-subrc = 0.
ASSIGN COMPONENT |level{ i_2 }| OF STRUCTURE <fs_sheet> TO FIELD-SYMBOL(<fs_level1>).
IF sy-subrc = 0.
ASSIGN COMPONENT |level{ i_3 }| OF STRUCTURE <fs_level1> TO FIELD-SYMBOL(<fs_level2>).
IF sy-subrc = 0.
ASSIGN COMPONENT 'level4' OF STRUCTURE <fs_level2> TO FIELD-SYMBOL(<fs_level3>).
IF sy-subrc = 0.
<fs_level3> = 'some_value'.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDMETHOD.
ENDCLASS.
DEFINE test.
lcl_test=>test(
EXPORTING
i_1 = &1
i_2 = &2
i_3 = &3
CHANGING
cs_root = &4
).
END-OF-DEFINITION.
DATA: gs_root TYPE t1.
START-OF-SELECTION.
DO 14 TIMES.
test 1 2 sy-index gs_root.
ENDDO.
使用动态规划,将您的 fill
宏更改为:
DATA l_field TYPE string.
FIELD-SYMBOLS <l_value> TYPE any.
DEFINE fill.
l_field = &3.
CONDENSE l_field.
CONCATENATE 'cs_root-sheet&1-&2-level' l_field '-&4' INTO l_field.
ASSIGN (l_field) TO <l_value>.
IF sy-subrc = 0.
<l_value> = 'some_value'.
ELSE.
" TODO: error handling?
ENDIF.
END-OF-DEFINITION.
这会起作用,尽管您可能想在 ASSIGN
之后检查 sy-subrc
,因为该参数是不变的(仅在运行时已知),因此不会在编译时发现错误,例如另一个参数会。
您还可以为 DO
循环的上限添加编译时验证,因为您因此知道 sy-index
的最大值。为此,您可以在 iterate_menges
宏中添加一个非执行引用:
DEFINE iterate_menges.
IF 1 = 0. " compile-time boundary validation test
cs_root-sheet&1-&2-level&4-level4 = ''.
ENDIF.
DO &4 TIMES.
fill &1 &2 sy-index level4.
ENDDO.
END-OF-DEFINITION.
第二种方法是添加 case 语句。仅当您知道该部分中始终存在一定数量的字段时才可以使用此方法(当然应该始终至少有一个...)。因此,如果您知道 DO
循环的下界,那么您可以编写以下代码作为优化:
CASE &3.
WHEN 1. " set 1
WHEN 2. " set 2
WHEN 3. " set 3
" ...
WHEN OTHERS.
" dynamic set
ENDCASE.
由于动态设置较慢,因此优化紧密循环始终是个好主意。
有一个用宏填充深层结构的任务,其中结构组件的名称相似,可以通过简单的索引循环构建。 比如结构为root-level1-level2-level3-level4
我想用下面的嵌套宏填充它
DEFINE iterate_menges.
do &4 times.
fill &1 &2 sy-index level4.
enddo.
END-OF-DEFINITION.
DEFINE fill.
cs_root-sheet&1-&2-level&3-&4 = 'some_value'.
END-OF-DEFINITION.
但是这个概念不起作用,sy-index
被当作文本处理。错误
component cs_root-sheet1-level2-levelsy-index-level4 is not found
已显示,但数字字面量效果非常好。
这里应该使用什么语法?
ADDITION: 这是我在 SCN 上找到的示例片段,它运行良好。为什么会这样?
DEFINE ADD_MAPPING.
p_c = &1.
CONDENSE p_c.
CONCATENATE 'p_old' p_c INTO p_c.
ASSIGN (p_c) TO <fs>.
WRITE <fs>.
END-OF-DEFINITION.
DO 14 TIMES.
ADD_MAPPING sy-index.
ENDDO.
P.S。是的,我知道宏是不可调试的、不安全的并且完全不应该使用,但我对这个特定问题感兴趣而不是最佳实践建议。
系统正在执行 documentation 中所述的操作。不幸的是,在这种情况下,英文翻译缺少一些细节,而不是德文原文,更重要的是,恕我直言。宏的使用不是某种调用,它是编译前发生的文本替换。参数被替换,而不是被评估 - 它们 不能 被评估,因为在大多数情况下,该值在编译时是未知的,仅在运行时是未知的。要做你想做的事,你将不得不使用像 ASSIGN COMPONENT ... OF ...
你想做的事是不可能的,因为宏只有在编译时才知道。它们不是 ABAP 中模块化手段的一部分。
人们甚至可以编写自己的 Brainfuck 版本。看下面的例子。
REPORT zzz.
TYPES: BEGIN OF t1,
sheet1 TYPE c,
sheet2 TYPE c,
END OF t1.
DATA:
cs_root TYPE t1.
DEFINE test.
cs_root-sheet&1 = 'costam'.
END-OF-DEFINITION.
DEFINE brainfuck.
&4=>&1&3 &2.
END-OF-DEFINITION.
START-OF-SELECTION.
brainfuck IF_SYSTEM_UUID_STATIC~CREATE_UUID_X16 ) ( CL_SYSTEM_UUID.
test sy-index.
在另一个答案下回答您的评论。解决方案可能如下所示。
REPORT zzz.
TYPES: BEGIN OF t4,
level4 TYPE c,
END OF t4.
TYPES: BEGIN OF t3,
level1 TYPE t4,
level2 TYPE t4,
END OF t3.
TYPES: BEGIN OF t2,
level1 TYPE t3,
level2 TYPE t3,
END OF t2.
TYPES: BEGIN OF t1,
sheet1 TYPE t2,
sheet2 TYPE t2,
END OF t1.
CLASS lcl_test DEFINITION FINAL.
PUBLIC SECTION.
CLASS-METHODS:
test
IMPORTING
i_1 TYPE i
i_2 TYPE i
i_3 TYPE i
CHANGING
cs_root TYPE t1.
ENDCLASS.
CLASS lcl_test IMPLEMENTATION.
METHOD test.
ASSIGN COMPONENT |sheet{ i_1 }| OF STRUCTURE cs_root TO FIELD-SYMBOL(<fs_sheet>).
IF sy-subrc = 0.
ASSIGN COMPONENT |level{ i_2 }| OF STRUCTURE <fs_sheet> TO FIELD-SYMBOL(<fs_level1>).
IF sy-subrc = 0.
ASSIGN COMPONENT |level{ i_3 }| OF STRUCTURE <fs_level1> TO FIELD-SYMBOL(<fs_level2>).
IF sy-subrc = 0.
ASSIGN COMPONENT 'level4' OF STRUCTURE <fs_level2> TO FIELD-SYMBOL(<fs_level3>).
IF sy-subrc = 0.
<fs_level3> = 'some_value'.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDMETHOD.
ENDCLASS.
DEFINE test.
lcl_test=>test(
EXPORTING
i_1 = &1
i_2 = &2
i_3 = &3
CHANGING
cs_root = &4
).
END-OF-DEFINITION.
DATA: gs_root TYPE t1.
START-OF-SELECTION.
DO 14 TIMES.
test 1 2 sy-index gs_root.
ENDDO.
使用动态规划,将您的 fill
宏更改为:
DATA l_field TYPE string.
FIELD-SYMBOLS <l_value> TYPE any.
DEFINE fill.
l_field = &3.
CONDENSE l_field.
CONCATENATE 'cs_root-sheet&1-&2-level' l_field '-&4' INTO l_field.
ASSIGN (l_field) TO <l_value>.
IF sy-subrc = 0.
<l_value> = 'some_value'.
ELSE.
" TODO: error handling?
ENDIF.
END-OF-DEFINITION.
这会起作用,尽管您可能想在 ASSIGN
之后检查 sy-subrc
,因为该参数是不变的(仅在运行时已知),因此不会在编译时发现错误,例如另一个参数会。
您还可以为 DO
循环的上限添加编译时验证,因为您因此知道 sy-index
的最大值。为此,您可以在 iterate_menges
宏中添加一个非执行引用:
DEFINE iterate_menges.
IF 1 = 0. " compile-time boundary validation test
cs_root-sheet&1-&2-level&4-level4 = ''.
ENDIF.
DO &4 TIMES.
fill &1 &2 sy-index level4.
ENDDO.
END-OF-DEFINITION.
第二种方法是添加 case 语句。仅当您知道该部分中始终存在一定数量的字段时才可以使用此方法(当然应该始终至少有一个...)。因此,如果您知道 DO
循环的下界,那么您可以编写以下代码作为优化:
CASE &3.
WHEN 1. " set 1
WHEN 2. " set 2
WHEN 3. " set 3
" ...
WHEN OTHERS.
" dynamic set
ENDCASE.
由于动态设置较慢,因此优化紧密循环始终是个好主意。