Skip to Content

With Release 7.40 ABAP supports so called constructor operators. Constructor operators are used in constructor expressions to create a result that can be used at operand positions. The syntax for constructor expressions is

… operator type( … ) …

operator is a constructor operator. type is either the explicit name of a data type or the character #. With # the data type can be dreived from the operand position if the operand type is statically known. Inside the parentheses specific parameters can be specified.

Value Operator VALUE

The value operator VALUE is a constructor operator that constructs a value for the type specified with type.

  • … VALUE dtype|#( ) …

    

constructs an initial value for any data type.

 

  • … VALUE dtype|#( comp1 = a1 comp2 = a2 … ) …

constructs a structure where for each component a value can be assigned.

  • … VALUE dtype|#( ( … ) ( … ) … ) …

constructs an internal table, where for each line a value can be assigned. Inside inner parentheses you can use the syntax for structures but not the syntax for table lines directly. But you can nest VALUE operators.

Note that you cannot construct elementary values (which is possible with instantiation operator NEW) – simply because there is no need for it.

For internal tables with a structured line type there is a short form that allows you to fill columns with the same value in subsequent lines

VALUE dtype|#( col1 = dobj11 … ( col2 = dobj12 col3 = dobj13 … )
                                 ( col2 = dobj22 col3 = dobj23 … )
                                   …
               col1 = dobj31 col2 = dobj32 … ( col3 = dobj33 … )
                                               ( col3 = dobj43 … )
               … ).

Example for initial values

Why would you like to construct an initial value anyhow? Well, you can pass an initial actual parameter to a structured or tabular formal parameter without the need of an initial hleper variable now.

CLASS c1 DEFINITION.
  PUBLIC SECTION.
    TYPES: BEGIN OF t_struct,
             col1 TYPE i,
             col2 TYPE i,
           END OF t_struct.
    CLASS-METHODS m1 IMPORTING p TYPE t_struct.
ENDCLASS.

CLASS c1 IMPLEMENTATION.
  METHOD m1.
    …
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

  c1=>m1( VALUE #( ) ).

Example for structures

Three different ways to construct the same nested structure:

TYPES:  BEGIN OF t_col2,
           col1 TYPE i,
           col2 TYPE i,
        END OF t_col2.

TYPES: BEGIN OF t_struct,
         col1 TYPE i,
         col2 TYPE t_col2,
       END OF t_struct.

DATA: struct TYPE t_struct,
      col2 TYPE t_col2.

“1
struct = VALUE t_struct( col1 = 1
                         col2-col1 = 1
                         col2-col2 = 2 ).

“2

col2   = VALUE   t_col2( col1 = 1
                         col2 = 2 ).
struct = VALUE t_struct( col1 = 1
                         col2 = col2 ).

“3

struct = VALUE t_struct( col1 = 1
                         col2 = VALUE #( col1 = 1
                                         col2 = 2 ) ).

Examples for internal tables

Elementary line type:

TYPES t_itab TYPE TABLE OF i WITH EMPTY KEY.

DATA itab TYPE t_itab.

itab = VALUE #( ( ) ( 1 ) ( 2 ) ).

Structured line type (RANGES table):

DATA itab TYPE RANGE OF i.

itab = VALUE #( sign = ‘I’  option = ‘BT’ ( low = 1  high = 10 )
                                          ( low = 21 high = 30 )
                                          ( low = 41 high = 50 )
                            option = ‘GE’ ( low = 61 )  ).

Other expressions in VALUE operator

Of course, the arguments of VALUE can be expressions or function calls:

 

TYPES t_date_tab TYPE TABLE OF string  WITH EMPTY KEY.

DATA(date_tab) = VALUE t_date_tab(
  ( |{ CONV d( sy-datlo – 1 ) DATE = ENVIRONMENT }| )
  ( |{         sy-datlo       DATE = ENVIRONMENT }| )
  ( |{ CONV d( sy-datlo + 1 ) DATE = ENVIRONMENT }| ) ).

So you can do a lot of crazy things now, but be aware of obfuscation …

To report this post you need to login first.

54 Comments

You must be Logged on to comment or reply to a post.

  1. Katan Patel

    Really handy little feature.  Thanks for sharing that.  I’ll be looking to use that to reduce some of the coding for my local class data.  Also liking the fact we can use expressions for the assignment. 

    (0) 
        1. Former Member

          Can do:

          var = gr_foo->bar(baz).

          will try to find a public attribute named bar and transfer the first baz characters into var.

          var = gr_foo->bar( baz )

          will call a method named bar and transfer baz as a parameter.

          (0) 
            1. Horst Keller Post author

              CLASS class DEFINITION.
                PUBLIC SECTION.
                  METHODS main.
                  METHODS meth IMPORTING p1 TYPE data
                          RETURNING VALUE(r) TYPE string.
              ENDCLASS.

              CLASS class IMPLEMENTATION.
                METHOD main.
                  DATA meth TYPE string.
                  DATA res  TYPE string.

                  res = meth(2).    “Offset/Length

                  res = meth( 2 )“Method Call

                ENDMETHOD.
                METHOD meth.
                ENDMETHOD.
              ENDCLASS.

              (0) 
                1. Horst Keller Post author

                  And the global vars ๐Ÿ˜‰

                  DATA meth TYPE string.

                  CLASS class DEFINITION.
                    PUBLIC SECTION.
                      METHODS main.
                      METHODS meth IMPORTING p1 TYPE data
                              RETURNING VALUE(r) TYPE string.
                  ENDCLASS.

                  CLASS class IMPLEMENTATION.
                    METHOD main.
                      DATA res  TYPE string.

                      res = meth(2).    “Offset/Length

                      res = meth( 2 )“Method Call

                    ENDMETHOD.
                    METHOD meth.
                    ENDMETHOD.
                  ENDCLASS.

                  (0) 
                    1. Suhas Saha

                      Uwe Fetzer wrote:

                                             

                      You know, nobody is using global variables anymore… ๐Ÿ˜ˆ

                                         

                      Yeah, in Utopia ๐Ÿ˜›

                      (0) 
            2. Former Member

              Assuming that the ABAP compiler works like other compilers do: It can’t, because checking whether a method or variable exists is part of the linking process, and the buildup of the AST and the symbol tables happens way before that. If someone is interested in this, I might hack together a small blog article about this…

              (0) 
        2. Horst Keller Post author

          In fact, we have the problem for inline declarations. It is solved by the following rule:

          If a field named “data” is already declared in a context, the syntax “data(…)” at an operand position is not an inline declaration but an offset length access to “data”. Only if “data” is not yet declared, the syntax “data(data)” declares “data”.

          ๐Ÿ˜ฅ

          (0) 
    1. Hendrik Neumann

      Let’s just say: get rid of the mandatory blanks in between the brackets.. and while we’re at it I would love to see the mandatory blanks surrounding assigments gone as well ๐Ÿ˜‰ Something like this lv_number=  5.  is just more expressive than lv_number = 5. Did see that first used by Kent Beck and liked it right away…

      (0) 
      1. Horst Keller Post author

        Unfortunately, in non-Unicode programs variable names can contain symbols ๐Ÿ™ .

        Still have to support non-Unicode programs.

        (0) 
  2. Suhas Saha

    Hello Horst,

    Finally we have upgraded the ABAP Kernel to 740 & i have already started to get my hands dirty.

    I am using your examples as reference to my learning. Is there any guideline to using the concrete datatype(dtype) or # while using VALUE operator?

    For e.g., i can re-write your code as

    struct = VALUE #( col1 = 1
                       col2 = VALUE #( col1 = 1
                                       col2 = 2 )
                     ).


    What are your suggestions?


    BR,

    Suhas

    (0) 
    1. Horst Keller Post author

      Hi Suhas,

      The syntax of all constructor expressions is:

      … operator type(  … ) …

      where operator is NEW, VALUE, CONV,  etc. and type specifies the data type of the return value.

      For type you can either specify the name dtype of a concrete data type or #. With # you let the compiler deduce the appropriate data type for the operand position (operand type) where the constructor expression is used. This is appropriate in most cases and relieves you from finding and typing the name of the type yourself. Typical example: a method meth expects a structured input parameter of type mystruct. When calling the method you can write

      meth( value #( col1 = … col2 = … )  )

      The compiler implicitly replaces # by mystruct. You could also write

      meth( value mystruct( col1 = … col2 = … )  )

      but that is not necessary here. So, you always use #, if you want to use the operand type and it can be deduced by the compiler.

      You use the explicit type, if you want to override the implicit operand type (normally not a use case), if you want to make your coding more clear by expressing the type explicitly, or if the compiler cannot deduce the operand type and you simply cannot use #. Example:

      DATA(struct) = mystruct( col1 = … col2 = … ).

      Here you must tell the compiler what type of structure you want to construct. If struct is already declared, you can use #:

      struct = #( col1 = … col2 = … ).

      Regards

      Horst

      (0) 
      1. Suhas Saha

        Thanks for your reply

        if you want to make your coding more clear by expressing the type explicitly

        This is what i wanted to know, I think using # may lead to obfuscation especially when passing it to procedures ๐Ÿ˜

        (0) 
        1. Horst Keller Post author

          It depends also on the operator.

          For CONV,  # is exactly what you need in most cases.

          Before:

          DATA text TYPE c LENGTH 255.

          DATA helper TYPE string.

          helper = text.

          DATA(xstr) = cl_abap_codepage=>convert_to( source = helper ).

          After:

          DATA text TYPE c LENGTH 255.

          DATA(xstr) = cl_abap_codepage=>convert_to( source = CONV #( text ) ).

          I don’t need to know what the type is and if the interface of the method is changed, your code has a good chance to survive.

          (0) 
          1. Suhas Saha

            I don’t need to know what the type is and if the interface of the method is changed, your code has a good chance to survive.

            Great tip Horst!

            (0) 
  3. Former Member

    Hi Horst,

    first of thank you for an absolutely amazing ABAP version. A lot of the feature come naturally and I use them on a daily basis.

    One thing I just tried that isn’t working as expected is the following:

    DATA(lv_date) = VALUE dats( ‘20140929’ ).

    I get a syntax error that dats is not a structure.

    I know I can rewrite this as:

    DATA lv_date type dats value ‘20140929’.

    but is there a way to get the first version working using the new features?

    Regards

        Adi

    (0) 
    1. Horst Keller Post author

      Hi Adi,

      The resaon for the syntax error is, that VALUE does not allow you “construct” elementary types. One might argue “why not?” and in fact I did so often, but up to now this restriction is kept.

      But the gap can simply be closed as follows:

      DATA(lv_date) = CONV dats( ‘20140929’ ).

      Best

      Horst

      (0) 
      1. Suhas Saha

        That’s a smart workaround Horst ๐Ÿ˜Ž

        Anyway was there any reason why it was decided why VALUE cannot be used to build the value of elementary types?

        (0) 
        1. Horst Keller Post author

          You should construct elementary values only where needed:

          … NEW elem_type(  elem_value ) …

          … VALUE table_type( … ( elem_value ) … ) …

          In all other elementary operand positions you can place the elementary operands directly, no need to construct them. The only use case – conversions – is handled by the dedicated conversion operator CONV. And be aware that you also get a syntax warning, if you use CONV superfluously, as e.g. here:

            DATA int TYPE i.
            int = CONV #( 555 ).

          Another solution would have been to overload VALUE in a way that makes CONV superfluous. But the decision was to offer two distinct operators.

          (0) 
  4. Jacques Nomssi

    Let us assume I create the following types

    TYPES: BEGIN OF ts_path,

              caller TYPE i,

              called TYPE i,

            END OF ts_path.

    TYPES: BEGIN OF ts_trace,

         id         TYPE satr_de_id,

         path       TYPE ts_path,

         mod        TYPE seocpdname,

       END OF ts_trace.

    With this, the following code compiles

    DATA(ls_call) = VALUE

          ts_trace( id = ‘m’

                   path = VALUE #( caller = 3

                                             called = 5 ) ).

    while this does not:

    DATA(ls_call2) = VALUE ts_trace(

         id = ‘m’

         path = VALUE #( caller = 3    “<— Error: Arithmetic calculation not permitted

                                   called = 5 )

         mod = ‘MAIN’ ).


    Now, is this a bug or a feature ?

    (the work around is to rename the field mod to something.. else).

    regards,

    JNN

    (0) 
    1. Horst Keller Post author

      Hi Jacques,

      Seems that you are Mr. Bugfinder, No.1.

      Writing it as

      TYPES: BEGIN OF struc1,
               col1 TYPE i,
               col2 TYPE i,
             END OF struc1.

      TYPES: BEGIN OF struc2,
               col1 TYPE c LENGTH 1,
               col2 TYPE struc1,
               col3 TYPE c LENGTH 10,
             END OF struc2.

      TYPES: BEGIN OF struc3,
               col1 TYPE c LENGTH 1,
               col2 TYPE struc1,
               mod  TYPE c LENGTH 10,
             END OF struc3.

      DATA(ls_call2) = VALUE struc2(
           col1 = ‘x’
           col2 = VALUE #( col1 = 3
                           col2 = 5 )
           col3 = ‘XXX’ ).

      DATA(ls_call3) = VALUE struc3(
           col1 = ‘x’
           col2 = VALUE #( col1 = 3    “<— Error: Arithmetic calculation not permitted
                           col2 = 5 )
           mod = ‘XXX’ ).

      clearly hints to a bug.

      I forwarded to development.

      Cheers!

      Horst

      B.T.W., replacing mod with div gives the same error.

      (0) 
  5. Arshad Ansary

    Hi Horst,

    Thanks for sharing the new syntax in strcutred manner

    just out of curiosity I did a perfomance analysis of the append . The new syntax was taking thrice time of the old syntax.


    TYPES ty_itab_value TYPE TABLE OF i WITH EMPTY KEY.

    DATA itab_value TYPE ty_itab_value.

    get RUN TIME FIELD DATA(lv_fld1).

    itab_value = VALUE #( ( ) ( 1 ) ( 2 ) ).

    get RUN TIME FIELD DATA(lv_fld2).

    DATA(lv_fld_diff) = lv_fld2 lv_fld1.

    WRITE: / lv_fld_diff.

    clear:itab_value.

    get RUN TIME FIELD DATA(lv_fld3).

    APPEND 0 to itab_value.

    APPEND 1 to itab_value.

    APPEND 2 to itab_value.

    get RUN TIME FIELD DATA(lv_fld4).

    DATA(lv_fld_diff1) = lv_fld4 lv_fld3.

    WRITE: / lv_fld_diff1.


    The result was 3:1. Ofcourse this is not a serious test(but without scalable data) . Is there any doc where we have perf impications comapred to older syntax.


    Regards

    Arshad

    (0) 
    1. Uwe Fetzer

      Hi Arshad,

      not a serious test


      hehe, you are right ๐Ÿ˜‰


      I’ve tested it within a loop (DO 10 million TIMES…)  and the ration changed to 4:3. The new syntax is still a bit slower, but we are talking about milliseconds, so not really a problem, isn’t it?

      (0) 
    2. Horst Keller Post author

      With your “test” you can also show the opposite behavior ๐Ÿ™‚

      TYPES ty_itab_value TYPE TABLE OF i WITH EMPTY KEY.

      DATA itab_value TYPE ty_itab_value.

      “itab_value = VALUE #( ( ) ( 1 ) ( 2 ) ).

      clear:itab_value.

      get RUN TIME FIELD DATA(lv_fld3).

      APPEND 0 to itab_value.

      APPEND 1 to itab_value.

      APPEND 2 to itab_value.

      get RUN TIME FIELD DATA(lv_fld4).

      DATA(lv_fld_diff1) = lv_fld4 – lv_fld3.

      WRITE: / lv_fld_diff1.

      clear:itab_value.

      get RUN TIME FIELD DATA(lv_fld1).

      itab_value = VALUE #( ( ) ( 1 ) ( 2 ) ).

      get RUN TIME FIELD DATA(lv_fld2).

      DATA(lv_fld_diff) = lv_fld2 – lv_fld1.

      WRITE: / lv_fld_diff.

      You measure the creation of the internal table in your first go that takes more time than filling it.

      If you take away the ” above, the internal table is created before both measurements …

      (0) 
  6. Former Member

    Hi Horst,

    Before 7.4 the below code inserted a new line into table itab.

    Start-of-selection.

         my_class=>my_method( changing ct_tab = itab ).

    Method my_method.

         Insert c1=>m1( ) into table ct_tab.               “ct_tab is changing param

    Endmethod.

    Now in 7.4 this code overwrites the content of ct_tab instead of inserting a new line.

    Method my_method.

         ct_tab = VALUE #( ( c1=>m1( ) ) ).

    Endmethod.

    Where:

    Method m1.

         rs_line = ls_line.               “rs_line is returning param

    Endmethod.

    I want to insert a new line into the itab – where am I going wrong?

    Regards,

    Sougata.

    (0) 
      1. Former Member

        I was working with the 7.4 example:

        Before 7.4:

        Data:

            itab type tt,

            wa  like line of itab.

        wa-c1 = 7.

        wa-c2 = 9.

        INSERT wa INTO TABLE itab.

        wa-c1 = 3.

        wa-c2 = 5.

        INSERT wa INTO TABLE itab.

        Instead in 7.4:

             Data(itab) = VALUE tt( ( c1 = 7 c2 = 9 )

                                                  ( c1 = 3 c2 = 5 ) ).

        Is it not possible to use the same inside a method instead of INSERT or APPEND?

        (0) 
  7. Former Member

    When you construct the structure with the operator #, this code cannot be found via where-used list.

    Example:

    lo_obj->set_structure( VALUE #( field1 = ‘aaa’ field2= ‘bbb” ).
    The type of importing structure is STRUCT_TYPE

    When you enter where used list on STRUCT_TYPE, this line of code does not appear.

    It can be “solved” via replacing # with STRUCT_TYPE:

    lo_obj->set_structure( VALUE struct_type( field1 = ‘aaa’ field2= ‘bbb” ).

    but it’s not the nicest workaround, I think.

    Regards,

    Radek

    (0) 
  8. Former Member

    Hi Horst

    I’v been using this new syntax for a while now, and until today everything was fine, but today I stumbled upon something that surprised me, the next line doesn’t compile:

    data(itab) = VALUE string_table( ( ‘string1’ ) ( ‘string2’ ) ).


    Error is “‘string1′” and the line of “ITAB” are incompatible., while this other line does compile:


    data(itab) = VALUE string_table( ( |string1| ) ( |string2| ) ).


    I guess it has something to do with Text field literals and Text string literals, but still find it weird as all other assigments between text field literals and strings work, even when calling methods like this:


    CLASS lcl_test DEFINITION.

       PUBLIC SECTION.

         CLASS-METHODS test IMPORTING st TYPE string.

    ENDCLASS.

    CLASS lcl_test IMPLEMENTATION.

       METHOD test.

         WRITE st.

       ENDMETHOD.

    ENDCLASS.


    START-OF-SELECTION.

    lcl_test=>test( ‘string’ ).


    So, any ideas on why this happens?


    btw thanks for the blog, I find it very interesting.

    Regards

    Simón

    .

    (0) 
  9. Evgeniy Astafev

    Hi Horst.

    Is there any way to debug step by step the VALUE operator? Like LOOP AT.

    i.e.

    data(z) = value t_table( for wa in some_table where ( <some condition> )

                                            ( field1 = cond #( when wa-field1 > 0 then x else y ) ).

    (0) 
      1. Former Member

        TYPES: BEGIN OF ty_ship,
                   tknum
        TYPE tknum,     “Shipment Number
                   name 
        TYPE ernam,     “Name of Person who Created the Object
                   city 
        TYPE ort01,     “Starting city
                   route
        TYPE route,     “Shipment route
              
        END OF ty_ship.
        TYPES: ty_ships TYPE SORTED TABLE OF ty_ship WITH UNIQUE KEY tknum.
        TYPES: ty_citys TYPE STANDARD TABLE OF ort01 WITH EMPTY KEY.

        GT_SHIPS type ty_ships. -> has been populated as follows:

        Row TKNUM[C(10)] Name[C(12)] City[C(25)] Route[C(6)]
        2 002 Gavin Sydney R0003
        4 004 Elaine Perth R0003
        1 001 John Melbourne R0001
        3 003 Lucy Adelaide

        R0001


        DATA(gt_citys) = VALUE ty_citys( FOR ls_ship IN gt_ships ( ls_shipcity ) ).


        Above line will copy CITY column data into GT_CITYS.


        What we should do if we need Column ROUTE data along with CITY?.

        I want to copy all the lines of CITY and ROUTE data to another table.

        (0) 
  10. Timothy Muchena

    Hi thanks for your blogs

    On performance – is there a performance gain when you use VALUE expression to move internal table contents from itab1 to itab2 instead of looping through itab1 and appending to itab2?

    Regards

    (0) 
  11. Former Member

    Hi Horst,

    I’ve a question regarding VALUE with FOR:

        DATA(del) = VALUE tt_vbeln( FOR line IN deliveries (

                                       table_line  = line-document_numb ) ).

    An error is ocuring in “table_line”. It says “Type “C” is not a structure”.

    Can’t I use this?

    Thank you.

    Regards,

    Luís

    (0) 
    1. Horst Keller Post author

      Hi Luís,

      You cannot use the pseudo comment table_line inside the construction of a structured table line. The only position where table_line can be used in FOR is the WHERE condition.

      The syntax error stems from the fact, that you use the syntax for constructing a structure. But the line type of tt_vbeln is not structured.

      Instead, your code should simply worke like this:

      DATA(del) = VALUE tt_vbeln( FOR line IN deliveries

                                                            ( line-document_numb )  ).

      Horst

      (0) 
  12. Former Member

    Hello Horst,

    I would like to ask how I could use VALUE operator with โ€˜FOR wa IN itabโ€™, instead of traditional โ€˜Loop at itab assigning <fs>โ€™, where in the loop I could remove lines and modify fields based on the values from other fields.

    I tried with something like below, but it didnโ€™t copy all lines from itab2, but only this for which field3 has been modified.

    itab1 = VALUE #( FOR wa IN itab2 where ( field1 = '123' AND field2 = 'ABC' )
                      
                          ( field3 = COND #( WHEN wa-flag EQ 'X'
                                            THEN 'TRUE' ELSE 'FALSE' )
                       ).

    When I add ( wa ) to parameters or I used โ€˜BASE CORRESPONDING #( itab2 ) before โ€˜FORโ€™ โ€“ like below:

    itab1 = VALUE #( FOR wa IN itab2 where ( field1 = '123' AND field2 = 'ABC' )
                          ( wa )
                          ( field3 = COND #( WHEN wa-flag EQ 'X'
                                            THEN 'TRUE' ELSE 'FALSE' )
                       ).

    or:

    itab1 = VALUE #( base CORRESPONDING #( itab2 )
                          FOR wa IN itab2 where ( field1 = '123' AND field2 = 'ABC' )
    
                          ( field3 = COND #( WHEN wa-flag EQ 'X'
                                            THEN 'TRUE' ELSE 'FALSE' )
                       ).

    โ€ฆ then all the lines have beenย copied + few additional lines with changed values of field3. So eventually there was more lines in itab1 then in itab2.

    What would be the best approach for this kind of requirement ?

    Kind Regards
    Rafal

    (0) 

Leave a Reply