使用循环将参数传递给宏

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.

由于动态设置较慢,因此优化紧密循环始终是个好主意。