User Defined Functions
A User Defined Function in Business Rules! is an extremely useful tool. You can use functions to perform a series of similar tasks on one or more variables and return the changed variable, a result of the interaction of those variables on one another, or a combination of both. Generally a function is created by using a DEF statement. If the function only needs one line of code then there are no additional lines, the function stands on its own. More likely, however, is a multilined function, these also start with a DEF clause followed by various BR statements and end with an FNEND statement.
The lines of code between the DEF and the FNEND statements follow the same rules as "normal" BR code with a few exceptions relating to variables passed in to the function. If a variable is passed into a function it can be "by reference" or "by value".
Furthermore, using the Library Facility and Library statement, user defined functions can be called from one program to be used in another, meaning functions used in multiple programs do not need to be repeated in the code of each program, but only called and used from a central program, or library.
A By Value function variable can be passed to a function, but is not returned by the function. That is, any changes made to the variable in the called function have no affect on the variable in the calling program. The calling program passes just the value or content of the variable into the function, not the actual variable, so the called function has no way to change the real variable. When calling a function, if a parameter is being passed By Value, you may substitute a literal instead of a variable.
A By Reference function variable is sent to the function and whatever changes the function makes on the variable are returned to (occur within) the calling program. By Reference variables are designated in the DEF statement with a leading ampersand "&". The calling program passes a memory reference to the variable itself, giving the function access to modify the variable. Matrices passed to a function are always passed By Reference, because it is much faster to pass just a reference to a large array into a function than it would be to pass the entire array By Value.
If a function definition lists a parameter as By Reference (you see the "&" in the def statement), then BR will not allow you to pass a literal as that parameter into the function. Instead you must pass an actual calling program variable.
As mentioned above a matrix is always passed By Reference. It can be redimensioned, changed in many ways and then used by the calling program in it's changed state.
The only time when this will not happen is when the matrix is non-existent in the function call. A non-existent matrix is one that has been marked as an "Optional" variable in the DEF statement and no matrix was named in the program call to the function. The non-existent matrix can not then be re-dimensioned because it does not exist. If you want to re-dimension a matrix and have it passed out of the function then it is necessary to use a "dummy" matrix passed in so that the function does not use a NULL array by default.
A function can either be a part of the program that is loaded, similar to a sub-routine, or it can be a library function that is either present in the program or located in another named program. A library function definition differs from a function definition because it includes the word library between the DEF and the name of the function. The program code for a library function is otherwise the same as a function.
In order to use a library function the library and the library function name must be designated in the program that calls the library function. (Libraries may call other libraries.) This is done with a "LIBRARY" statement.
The above statement says that the library functions FNPRINTBOX and FNDRAWBOX are located in the Business Rules! program "FNSNAP.dll" that has been named with a dll suffix, rather than a .BR or .WB suffix, located in the "wb\\vol002" directory of drive E:. Note that Business Rules! automatically assigns a .BR or .WB suffix to programs when they are initially created, but a BR program can have any suffix that you assign to it.
Any variable used with a program or library is common to all the functions, sub-routines and mainline routines within that program or library. This means that if, in library SAMPLE.br, library function FNFIRST uses a variable "X" to count how many times a routine is processed and it turns out that that number is 10, and library function FNSECOND, also included in SAMPLE.br, uses the variable "X" which it expects is initialized to zero because that function has not used it before, unexpected results will occur because the value of "X" is now 10, not zero. If FNSECOND were located in a different library, then the value of "X" set by FNFIRST would not be visible to it and FNSECOND would not use the value of 10, but might use a value previously set by FNSECOND.
It is good practice to avoid such problems by declaring 'work' variables as optional parameters within function definitions. Optional parameters are listed after required parameters and the two groups are separated with a semicolon (;). Listing work variables as optional parameters avoids both present and future variable name conflicts with other routines or functions, as long as no variable is 'brought into' the library function.
Sub-routines in programs are very useful. They can be used from different parts of the program without the necessity of duplicating code. They can also be used to keep complex manipulations out of the main logic of a program to make it easier to understand. Sub-routines can be used in functions, library functions, and libraries as well. Two library functions residing in the same library can each use the same sub-routine, located outside of the DEF / FNEND boundaries. This is true as long as the sub-routine is located within the same library as the library function(s) making use of it.
Once a library has been created and it is being named in a program it can be loaded as a "RESIDENT", library, a "CURRENT" library or a "RELEASE" library.
A resident library once loaded remains in memory and can be called by any program that is subsequently loaded. It retains its variable values from program to program. It is only removed with a clear command or by exiting the BR session.
00100 library resident "E:\\wb\\vol002\\getdates.br":FNDATE
The default load of a library is a current library. It is active while the program that loads it is in memory and retains its variables only while that program is active. A current library, because it terminates when a program ends, cannot call a resident library.
00100 library "E:\\wb\\vol002\\getdates.br":FNDATE
A library that is designated as release only maintains its variables while a call to it is active. As soon as the function reaches the FNEND statement and passes information back to the calling function or routine the variables are cleared. A released library, since it does not occupy memory when not active cannot call a current or a resident library.
00100 library release "E:\\wb\\vol002\\getdates.br":FNDATE