Passing records as parameter
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 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.
TYPE t_cust RECORD
cust_id INTEGER,
cust_name VARCHAR(50)
END RECORD
MAIN
DEFINE r t_cust
CALL initialize_customer(r)
DISPLAY r.*
END MAIN
FUNCTION initialize_customer(r t_cust INOUT) RETURNS ()
LET r.cust_id = 0
LET r.cust_name = "<undefined>"
END FUNCTION
See also the optimization topic Passing records by reference.
Passing records as single structured value
When only specifying the name of the record as parameter to a function, it is passed as a single structured value.
Functions should be been defined with the fully typed syntax.
TYPE t_cust RECORD
cust_id INTEGER,
cust_name VARCHAR(50)
END RECORD
MAIN
DEFINE r t_cust
CALL display_customer(r)
END MAIN
FUNCTION display_customer(r t_cust) RETURNS ()
DISPLAY r.*
LET r.cust_id = 999 -- has no effect
END FUNCTION
.*
notation, the compiler is more permissive and allows to pass and return record members with
different types (this is usually done by mistake). When using simple record names without
.*
as parameter, the compiler produces error -4325, if the types of the passed
record and the type of the function parameter do not match:TYPE t_cust RECORD
cust_id INTEGER,
cust_name VARCHAR(50)
END RECORD
MAIN
DEFINE r t_cust
CALL display_customer(r)
| The source and destination records in this record assignment statement are not compatible...
| See error number -4325.
END MAIN
FUNCTION display_customer(r)
DEFINE r RECORD
cust_id SMALLINT, -- Different type as t_cust.cust_id!
cust_name VARCHAR(50)
END RECORD
END FUNCTION
If the function is not know by the compiler (linker is used), the CALL
instruction with a simple record name paramerter will produce the compiler error -4340. To make the compiler check
with a valid call, the function must be declared in the same module, or it must be defined in a
module imported with IMPORT
FGL
.
FUNCTION (c t_cust) setLastOrderId(o t_order) RETURNS ()
LET c.last_order_id = o.order_id
END FUNCTION
Methods for types can only be defined with a fully typed syntax.
Passing records as single structured value to APIs
util.JSON.stringify()
use records passed as single structured
value:IMPORT util
...
DEFINE rec RECORD
cust_id INTEGER,
cust_name VARCHAR(50)
END RECORD
...
CALL util.JSON.stringify( rec )
...
Passing records implicitly by reference APIs
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 )
...
Passing records by expansion (.*
notation)
All elements of a RECORD
structure can be passed by value to a function with the
dot star (.*
) notation.
The record.*
parameter syntax is supported for backward compatibility. Consider
passing records by reference (INOUT
), 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.
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