Passing records as parameter

Passing records by expansion (.* notation)

All elements of a RECORD structure can be passed by value to a function with the dot star (.*) notation.

Note: The record.* parameter syntax is supported for backward compatibility. Consider passing records by reference, or by value by specifying the record name only.

When using the .* notation as function parameter, the record is expanded: Each member of the record structure is pushed on the stack. The receiving local variables in the function must be defined with the same record structure as the caller.

For backward compatibility, the runtime system allows to call functions defined with the individual parameters matching the record members (or the other way around - calling a function with individual values that match the record parameter definition of the function). However, this is bad practice.

Important: When using methods, the parameter type checking is enforced. It is not possible to pass records with the .* notation like with legacy functions: The compiler will reject the code, if the complex parameters types (records, arrays) do not match.

In the next example, the func_r() function is defined with a record parameter, while the func_ab() function is defined with individual integer and string parameters:

MAIN
    DEFINE rec RECORD
        a INT,
        b VARCHAR(50)
    END RECORD
    CALL func_r(rec.*)
    CALL func_ab(rec.*)     -- Bad practice!
    CALL func_r(101, 'aaa') -- Bad practice!
END MAIN

-- Function defining a record like that in the caller
FUNCTION func_r(r)
    DEFINE r RECORD
        a INT,
        b VARCHAR(50)
    END RECORD
    DISPLAY "func_r : ", r.*
END FUNCTION

-- Function defining two individual variables
FUNCTION func_ab(a, b)
    DEFINE
        a INT,
        b VARCHAR(50)
    DISPLAY "func_ab: ", a, b
END FUNCTION

Passing records by reference with INOUT

To pass records by reference, define a TYPE with the RECORD structure, and use this type in the parameter definition of the function, followed by the INOUT keyword.

The function must be known by the fglcomp compiler: It is not possible to use this feature for linked functions. The function defining the INOUT parameter must be declared locally in the same module, or it must be defined in a module imported with IMPORT FGL.

The function can then be invoked by specifying the record name as parameter.

The record structure defined in the caller can then be modified inside the function body:
TYPE CustRec RECORD
         cust_id INTEGER,
         cust_name VARCHAR(50)
     END RECORD

MAIN
    DEFINE r CustRec
    CALL initialize_customer(r)
    DISPLAY r.*
END MAIN

FUNCTION initialize_customer(r CustRec INOUT)
    LET r.cust_id = 0
    LET r.cust_name = "<undefined>"
END FUNCTION

See also the optimization topic Passing records by reference to functions.

Passing records by value

Records can be passed by value (without the .* notation) to methods for types:
FUNCTION (c t_cust) setLastOrderId(o t_order)
    LET c.last_order_id = o.order_id
END FUNCTION
Passing records by value is possible in API methods such as util.JSON.stringify():
IMPORT util
...
DEFINE rec RECORD
         cust_id INTEGER,
         cust_name VARCHAR(50)
       END RECORD
...
    CALL util.JSON.stringify( rec )
...

The record is not fully copied on the stack.

Passing records implicitly by reference to methods of built-in classes

With API methods such as util.JSON.parse(), the record is implicitly passed by reference, and it can be modified by the method:
IMPORT util
...
DEFINE rec RECORD
         cust_id INTEGER,
         cust_name VARCHAR(50)
       END RECORD
...
    CALL util.JSON.parse( '{"cust_id":2735, "cust_name":"McCarlson"}', rec )
...