Skip to Content
Technical Articles

ABAP Traps: Don’t self-reference in constructor operators

Just a short heads up on a bit of unexpected ABAP behaviour when using VALUE: Developers are used to assigning a variable’s content to itself, as in x = x + 1. This will not work with VALUE!

A quick test shows what happens:Β 

types: begin of ty_struct,
         val type i,
       end of ty_struct.

data(struct) = value ty_struct( val = 8 ).
write / struct-val.  " -> 8 

struct = VALUE #( val = struct-val + 1 ).
write / struct-val.  " -> not 9, but 1

Another related scenario would be to partially clear a structure. The following is a tempting way to do it but unfortunately will just result in an empty structure:

"Keep only F5 and F7, clear the rest:
struct = VALUE #( f5 = struct-f5 
                  f7 = struct-f7 ).  "values are lost, struct is empty

Examining the documentation closely, we can read:

If the VALUE operator is used as the source of an assignment to a structure, this structure is first initialized after any LET expressions are evaluated or the structure is first assigned the data object dobj after BASE. The assignments are then executed directly in the parenthesis, with the structure components as target fields. (my emphasis)

So it’s “working as designed”, just not as expected.

8 Comments
You must be Logged on to comment or reply to a post.
  • Nice Tip!

    To solve the first issue we can use the base option.

    struct = VALUE #( BASE struct val = struct-val + 1 ).

    For the second issue by using the LET

    struct = VALUE #( let temp_struct = struct in val = temp_struct-val + 1 ).

    I guess that is what the documentation you pasted is saying right?

  • Hi Mahesh,

    Thanks for your input, spot on. We must be very aware of whether we really want to BASE it on all fields. My example was perhaps a bit brief, spot the problem with this one:

    WHILE ...
      IF some_condition( ). 
        record = VALUE #( BASE record 
                          item_no = record-item_no + 1
                          F1 = input ).
      ELSE. 
        record = VALUE #( BASE record 
                          item_no = record-item_no + 1
                          F2 = input ).
      ENDIF.

    Β 

    Regarding using LET, this still declares a procedure-local variable anyway so for the scenario of clearing all but a few fields I would pull it out into a regular variable:

    data(just_the_facts) = value ty_struct( f5 = struct-f5 
                                            f7 = struct-f7 ). 
    struct = just_the_facts.

    To me this reads a bit easier than LET.

    • πŸ˜€ True, But using LET we can write the code in a single line :p

      Reg. the puzzle(:D), i tried that in the system as well, couldn’t solve it πŸ™ πŸ™

       

      • The clue was in the WHILE statement. Since BASE applies to the whole record, an assignment from the previous loop pass will remain if the condition changes from one iteration to the next.

        So let’s say we’re appending a series of inputs to a table and checking if they are numeric. If numeric then put it in F1 else in F2.

        The first one is 123 -> F1
        The next one is 456 -> F1
        The next one is ABC -> F2 … but now this record will also have 456 in F1.

        This may or may not be what you want, but usually no. If the developer is not paying attention this could turn into a difficult bug, especially as the intent is not clear from the code.