RDML Built-In Function Tips and Techniques
Initial Creation of an RDML Built-In Function
It is recommended that the RDML commands of an RDML Built-In Function be in the following sequence:
FUNCTION OPTIONS( *DIRECT … *BUILTIN …)
followed optionally by the override BIF name and description:
DEFINE FIELD(#BIF_NAME) TYPE(*CHAR) LENGTH(20) DESC(<BIF description>) DEFAULT(<BIF name>)
followed optionally by the required and then optional arguments:
DEFINE/DEF_LIST #BIF_ARGnn
followed optionally by the required and then optional return values:
DEFINE/DEF_LIST #BIF_RETnn
Note that a working list argument or return value is always required.
It is also recommended that the naming convention for working list argument counters be BIF_ALCnn and for working list return value counters be BIF_RLCnn
Note that these recommendations are followed in the function skeleton generated from the application template BBRDMLBIF supplied by LANSA.
Return Value Working Lists
Return value working lists are not cleared automatically as a result of either specifying them in a USE of an RDML Built-In Function or on entry to an RDML Built-In Function.
It is the responsibility of the RDML Built-In Function provider to either explicitly execute CLR_LIST commands against return value working lists in the RDML Built-In Function to document that entries are added on the end of the return value working list.
This behavior allows the RDML Built-In Function to be more flexible with regard to return value working lists.
Interactive Commands
It would be extremely unusual to code DISPLAY, POP_UP or REQUEST commands in an RDML BIF function, unless it was documented that the RDML BIF is to run only in an interactive environment and appropriate checking for such an environment were coded on entry to the RDML BIF. This would affect the portability of the RDML BIF.
Termination of RDML Built-In Function
An RDML Built-In Function that is *LIGHTUSAGE will terminate after every USE of it.
An RDML Built-In Function that is *HEAVYUSAGE will not terminate after every USE of it. Instead, the USEing function controls when it ends. If the using function is *HEAVYUSAGE it will not terminate until the program is shutdown, e.g. EXIT, but RDML commands are not executed when shutting down, so the BIF shutdown logic will not be executed. If the using function is *LIGHTUSAGE, there will be a termination call made to the BIF. Such an RDML Built-In Function can have its evaluation logic conditioned by testing that the system variable *BIF_SHUTDOWN is not 'Y'. Any special shutdown logic can be conditioned by testing that *BIF_SHUTDOWN is 'Y'.
Any RDML Built-In Function can refer to the system variable *BIF_SHUTDOWN. For RDML Built-In Functions that are *LIGHTUSAGE, its value will always be 'N', and consequently any special shutdown logic conditioned by its value being 'Y' will never be executed.
When an RDML Built-In Function is changed from *LIGHTUSAGE to *HEAVYUSAGE, or vice versa, all functions that use the RDML Built-In Function should be re-compiled if there is shutdown logic in the RDML Built-In Function.
These last three points are important. The execution of the shutdown logic is dependent not just on whether the RDML Built-In Function is marked as light or heavy usage. It also depends on whether the USEing function is light or heavy usage. The recommendation is to specify *LIGHTUSAGE in the RDML Built-In Function and to not have any logic in the shutdown logic. This means that the whole function becomes conditioned on *BIF_SHUTDOWN being 'N'. If the RDML Built-In Function is then changed to *HEAVYUSAGE, the USEing functions do not need to be re-compiled. Note that the impact of field values retaining their value between invocations would need to be investigated. The best solution to this is to always use explicit CHANGE/ASSIGN commands to set the initial value of all fields used in the function.
Platform Differences when Shutting down *HEAVYUSAGE Built-In Functions
There are differences when RPG functions as opposed to C/C++ functions shutdown *HEAVYUSAGE functions. (RPG is generated for RDML functions on IBM i. C/C++ is generated for RDMLX functions on IBM i, and both RDML and RDMLX functions on non-IBM i.)
An RPG function, when shutting down, will shutdown all *HEAVYUSAGE functions it refers to.
A C/C++ function, when shutting down, will shutdown only those *HEAVYUSAGE functions it has used while executing.
The corollary of this is that the shutdown code in the *HEAVYUSAGE Built-In Function must understand that it may be called when it hasn't been used.
An RPG function that is *HEAVYUSAGE will not get called to shutdown on IBM i unless a *LIGHTUSAGE functions uses it. Thus when the application is run again, it will still have the same state as the applications previous execution, unless Reclaim Resources has been run (RCLRSC) which will clear the state but not actually run the shutdown code.
A C/C++ Function that is *HEAVYUSAGE will get called to shutdown even when the application is terminating.
State Retention in *HEAVYUSAGE Built-In Functions
The state of fields used in *HEAVYUSAGE functions should not be relied upon due to the termination behaviour described above. For example, incrementing a value each time the RDML Built-In Function is executed. A *HEAVYUSAGE RDML Built-In Function can be called by *HEAVYUSAGE Functions and accumulate state. But as soon as a *LIGHTUSAGE Function is used, the *HEAVYUSAGE RDML Built-In Function will be shutdown and thus lose its state.
If you need to retain state, use a data area or system variable.
Optional Arguments and Return Values
It would be usual to condition references to optional return values and their derivation by testing the value of the system variable *BIF_RETCOUNT. In particular where deriving an optional return value is expensive in terms of machine resources, this would be more efficient.
Optional arguments, if not passed, will have their default value. References to optional arguments can be conditioned by testing the value of the system variable *BIF_ARGCOUNT. In particular, if the default value is not distinguishable from a passed value, but processing still needs to be different, this would be necessary.
Optional arguments and return values can be referred to in the RDML Built-In Function whether or not they have been coded in the USE command of the USEing function.
Access to an RDML BIF Function
Functions are partition specific. Built-In Function definitions are LANSA system wide. If an RDML Built-In Function has to be made available to a partition other than that where it has been compiled, it must be accessible in the library list for that other job. As with 3GL BIFS, this is the responsibility of the LANSA administrator or provider of the BIF.
Replacing 3GL BIFs with RDML BIFS
If, in the interests of portability, a 3GL BIF is replaced by an RDML BIF, all functions that USE that BIF will need to be recompiled, because of differences in the underlying architecture between 3GL and RDML BIFs
Adding Optional Arguments and Return Values
New optional arguments and return values can be added to RDML Built-In Functions without having to recompile all functions that already USE the RDML BIF. To do this the new optional arguments or return values are added after all other arguments and return values. Obviously functions that take advantage of the new optional arguments or return values will need to be recompiled. However, if the Built-In Function is enabled for RDMLX and it is used from any RDML function, then you cannot add an extra parameter, or in fact change anything to do with the number and order of arguments and return values, unless you recompile the RDML functions that USE this RDMLX BIF.
All other combinations are OK. That is:
-
RDMLX functions to RDMLX Built-In Functions
-
RDMLX functions to RDML Built-In Functions
-
RDML functions to RDML Built-In Functions.
RDML BIFS in a Client / Server Environment
An RDML BIF can be used to redirect execution from the client to the server without the remainder of the application being concerned with the 'mechanics' of it.